553 Stimmen

Warum verbietet C# generische Attributtypen?

Dies führt zu einer Ausnahme bei der Kompilierung:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Ich weiß, dass C# keine generischen Attribute unterstützt. Allerdings, nach viel Googeln, kann ich nicht scheinen, um den Grund zu finden.

Weiß jemand, warum sich generische Typen nicht von Attribute ? Irgendwelche Theorien?

383voto

Jon Skeet Punkte 1325502

Nun, ich kann nicht sagen, warum es nicht verfügbar ist, aber ich puede bestätigen, dass es sich nicht um ein CLI-Problem handelt. Die CLI-Spezifikation erwähnt es nicht (soweit ich sehen kann) und wenn Sie IL direkt verwenden, können Sie ein generisches Attribut erstellen. Der Teil der C# 3 Spezifikation, die es verbietet - Abschnitt 10.1.4 "Class base specification" gibt keine Begründung.

Die kommentierte ECMA C# 2 Spezifikation gibt auch keine hilfreichen Informationen, obwohl sie ein Beispiel dafür liefert, was nicht erlaubt ist.

Mein Exemplar der kommentierten C# 3-Spezifikation sollte morgen eintreffen... Ich werde sehen, ob das mehr Informationen gibt. Wie auch immer, es ist definitiv eine Sprache Entscheidung eher als eine Laufzeit ein.

EDIT: Antwort von Eric Lippert (paraphrasiert): kein besonderer Grund, außer um Komplexität in der Sprache und im Compiler für einen Anwendungsfall zu vermeiden, der keinen großen Mehrwert bringt.

92voto

GalacticCowboy Punkte 11505

Ein Attribut schmückt eine Klasse zur Kompilierungszeit, aber eine generische Klasse erhält ihre endgültigen Typinformationen erst zur Laufzeit. Da das Attribut die Kompilierung beeinflussen kann, muss es zur Kompilierungszeit "vollständig" sein.

Siehe dies MSDN-Artikel für weitere Informationen.

22voto

GeekyMonkey Punkte 11711

Ich weiß nicht, warum es nicht erlaubt ist, aber dies ist eine mögliche Lösung

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}

[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}

18voto

nawfal Punkte 65966

Dies ist nicht wirklich generisch und Sie müssen immer noch spezifische Attributklassen pro Typ schreiben, aber Sie können eine generische Basisschnittstelle verwenden, um ein wenig defensiv zu programmieren, weniger Code als sonst erforderlich zu schreiben, die Vorteile der Polymorphie zu nutzen usw.

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}

17voto

Hossein Ebrahimi Punkte 318

Generische Attribute wurde zu C# 10 hinzugefügt als eine Vorschau Funktion, das heißt, Sie müssen die <LangVersion> zur Vorschau um diese Funktion zu aktivieren; und die Funktion kann sich bis zur endgültigen Freigabe noch ändern.

Beachten Sie, dass es einige Einschränkungen für diese Funktion gibt:

Sie können ein vollständig geschlossen konstruiertes generisches Attribut anwenden. Mit anderen Worten: Alle Typparameter müssen angegeben werden. Folgendes ist zum Beispiel nicht erlaubt:

public class GenericType<T>
{
   [GenericAttribute<T>()] // Not allowed! generic attributes must be fully closed types.
   public string Method() => default;
}

y

Die Typargumente müssen die gleichen Einschränkungen erfüllen wie der typeof-Operator. Typen, die Metadaten-Annotationen erfordern, sind nicht zulässig. Beispiele sind die folgenden:

  • dynamic
  • nint , nuint
  • string? (oder ein beliebiger nullbarer Referenztyp)
  • (int X, int Y) (oder beliebige andere Tupel-Typen mit C#-Tupel-Syntax).

Diese Typen sind nicht direkt in den Metadaten enthalten. Sie enthalten Anmerkungen, die den Typ beschreiben. In allen Fällen können Sie stattdessen den zugrunde liegenden Typ verwenden:

  • object para dynamic .
  • IntPtr 代わりに nint o unint .
  • string 代わりに string? .
  • ValueTuple<int, int> 代わりに (int X, int Y) .

Source : https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#generic-attributes

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X