So erstellen Sie ein benutzerdefiniertes Attribut in C #

117

Ich habe viele Male versucht, aber ich bin immer noch nicht in der Lage, die Verwendung von benutzerdefinierten Attributen zu verstehen (ich habe bereits viele Links durchlaufen).

Kann mir bitte jemand ein sehr einfaches Beispiel für ein benutzerdefiniertes Attribut mit Code erklären?

Slash Shogdhe
quelle

Antworten:

95

Während der Code zum Erstellen eines benutzerdefinierten Attributs ziemlich einfach ist, ist es sehr wichtig, dass Sie verstehen, was Attribute sind:

Attribute sind Metadaten, die in Ihrem Programm kompiliert wurden. Attribute selbst fügen einer Klasse, Eigenschaft oder einem Modul keine Funktionalität hinzu - nur Daten. Mithilfe der Reflexion können diese Attribute jedoch genutzt werden, um Funktionen zu erstellen.

Schauen wir uns zum Beispiel den Validierungsanwendungsblock aus der Microsoft Enterprise Library an . Wenn Sie sich ein Codebeispiel ansehen, sehen Sie:

    /// <summary>
    /// blah blah code.
    /// </summary>
    [DataMember]
    [StringLengthValidator(8, RangeBoundaryType.Inclusive, 8, RangeBoundaryType.Inclusive, MessageTemplate = "\"{1}\" must always have \"{4}\" characters.")]
    public string Code { get; set; }

Aus dem obigen Snippet könnte man schließen, dass der Code immer validiert wird, wenn er gemäß den Regeln des Validators geändert wird (im Beispiel mindestens 8 Zeichen und höchstens 8 Zeichen). Aber die Wahrheit ist, dass das Attribut nichts tut; Wie bereits erwähnt, werden der Eigenschaft nur Metadaten hinzugefügt.

Die Unternehmensbibliothek verfügt jedoch über eine Validation.ValidateMethode, die Ihr Objekt untersucht und für jede Eigenschaft prüft, ob der Inhalt gegen die durch das Attribut angegebene Regel verstößt.

So sollten Sie also über Attribute nachdenken - eine Möglichkeit, Ihrem Code Daten hinzuzufügen, die später möglicherweise von anderen Methoden / Klassen / etc. Verwendet werden.

Bruno Brant
quelle
Wird mir die Antwort wirklich gefallen und besonders ", eine weitere Frage, die ich in die Set-Anweisung des obigen Codes einfügen kann, damit sie sich von den Attributen unterscheidet,
Slash Shogdhe
1
@slash: Kannst du das umformulieren? Ich habe die Frage nicht ganz verstanden.
Bruno Brant
1
Ich denke, ein Schrägstrich sollte nach dem Unterschied zwischen der Verwendung von Attributen und dem Einfügen des tatsächlichen Validierungscodes in den Eigenschaftssetter fragen. Antwort: Während das Schreiben von Code in den Setter zur Validierung des Werts durchgeführt werden kann, führt die Verwendung von Attributen allein keine Validierung als solche durch. Attribute sind nur "Metadaten". Ein anderer Code an einer anderen Stelle sollte an den von Ihnen verwendeten Attributen interessiert sein, diese lesen und darauf basierende Aktionen ausführen. Ein typisches Beispiel ist eine Validierungsbibliothek, wie @BrunoBrant erwähnt.
Romar
10
Ich bin mir nicht sicher, warum dies die akzeptierte Antwort ist. Die eigentliche Frage (die auch in Google indiziert ist) lautet "So erstellen Sie ein benutzerdefiniertes Attribut in C #". Die Antworten befassen sich überhaupt nicht mit diesem Thema. Die zweite Antwort dagegen schon.
Drakestar
Ich denke, die zweite Antwort bezieht sich eher auf die Frage.
Mohammad Taherian
267

Sie beginnen mit dem Schreiben einer Klasse, die von Attribute abgeleitet ist :

public class MyCustomAttribute: Attribute
{
    public string SomeProperty { get; set; }
}

Dann können Sie alles (Klasse, Methode, Eigenschaft, ...) mit diesem Attribut dekorieren:

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{

}

und schließlich würden Sie Reflexion verwenden, um es zu holen:

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // TODO: Do something with the value
}

Sie können die Zieltypen, auf die dieses benutzerdefinierte Attribut angewendet werden kann, mithilfe des Attributs AttributeUsage einschränken :

/// <summary>
/// This attribute can only be applied to classes
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute

Wichtige Informationen zu Attributen:

  • Attribute sind Metadaten.
  • Sie werden zur Kompilierungszeit in die Assembly eingebrannt , was sehr schwerwiegende Auswirkungen darauf hat, wie Sie ihre Eigenschaften festlegen können. Es werden nur konstante (zur Kompilierungszeit bekannte) Werte akzeptiert
  • Die einzige Möglichkeit, benutzerdefinierte Attribute zu verstehen und zu verwenden, ist die Verwendung von Reflection . Wenn Sie also zur Laufzeit keine Reflektion verwenden, um sie abzurufen und etwas mit einem benutzerdefinierten Attribut zu dekorieren, erwarten Sie nicht, dass viel passiert.
  • Der Zeitpunkt der Erstellung der Attribute ist nicht deterministisch. Sie werden von der CLR instanziiert und Sie haben absolut keine Kontrolle darüber.
Darin Dimitrov
quelle
3
Wo, in welcher Funktion / Klasse soll ich "Reflexion verwenden, um es zu holen"
Hasan A Yousef
@Hasan A Yousef, zum Beispiel gibt es im Entity Framework das Attribut "Key", das dem Framework sagt: Diese Eigenschaft sollte als Primärschlüssel betrachtet werden. Bei der Erstellung von ORM sind Attribute sehr hilfreich
Parsa
Wie greifen Sie auf ein benutzerdefiniertes Attribut für eine Eigenschaft und nicht auf eine Klasse zu?
Leinwand
docs.microsoft.com/en-us/dotnet/standard/attributes/… nur der Vollständigkeit halber fasst diese MSDN-Seite es sehr gut zusammen
Barış Akkurt
Mit Generika ist es viel, viel einfacher, die Typen zu bekommen:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh
27

Verwenden Sie die großartige Antwort von Darin Dimitrov , um auf ein benutzerdefiniertes Attribut für eine Eigenschaft und nicht für eine Klasse zuzugreifen:

Das dekorierte Eigentum [der Klasse Foo]:

[MyCustomAttribute(SomeProperty = "This is a custom property")]
public string MyProperty { get; set; }

Abrufen:

PropertyInfo propertyInfo = typeof(Foo).GetProperty(propertyToCheck);
object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
if (attribute.Length > 0)
{
    MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
    string propertyValue = myAttribute.SomeProperty;
}

Sie können dies in eine Schleife werfen und mithilfe der Reflektion auf dieses benutzerdefinierte Attribut für jede Eigenschaft der Klasse zugreifen Foo:

foreach (PropertyInfo propertyInfo in Foo.GetType().GetProperties())
{
    string propertyName = propertyInfo.Name;

    object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
    // Just in case you have a property without this annotation
    if (attribute.Length > 0)
    {
        MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
        string propertyValue = myAttribute.SomeProperty;
        // TODO: whatever you need with this propertyValue
    }
}

Vielen Dank an dich, Darin !!

Trichter
quelle
Wie würden wir dies erweitern, wenn wir nicht wissen, welche Arten von Attributen auf einer Eigenschaft vorhanden sind? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Ich möchte nur alle m1()
durchlaufen