2010-04-20 2 views
47

Meine Suchanfragen werden nur in Leitfäden angezeigt, in denen erläutert wird, wie Attribute in einer Klasse verwendet und angewendet werden. Ich möchte lernen, wie ich meine eigenen Attributklassen und die Mechanismen ihrer Funktionsweise erstellen kann.Wie funktionieren Attributklassen?

Wie werden Attributklassen instanziiert? Werden sie instanziiert, wenn die Klasse, auf die sie angewendet wird, instanziiert wird? Wird für jede implementierte Klasse eine Instanz instanziiert, auf die sie angewendet wird? Z.B. Wenn ich die SerializableAttribute-Klasse auf eine MyData-Klasse anwende und fünf MyData-Instanzen instanziiere, werden dann fünf Instanzen der SerializbleAttribute-Klasse hinter den Kulissen erstellt? Oder wird nur eine Instanz zwischen allen geteilt?

Wie greifen Attributklasseninstanzen auf die Klasse zu, der sie zugeordnet sind? Wie greift eine SerializableAttribute-Klasse auf die Klasse zu, auf die sie angewendet wird, damit sie ihre Daten serialisieren kann? Hat es eine Art von SerializableAttribute.ThisIsTheInstanceIAmAppliedTo Eigenschaft? :) Oder funktioniert es in der umgekehrten Richtung, dass, wenn ich etwas serialisiere, die Serialize-Funktion, an die ich die MyClass-Instanz übergebe, die Attribute durchläuft und die SerialableAttribute-Instanz findet?

+4

+1 Jeder für hilfreiche Antworten. Jetzt verstehe ich klar, dass Attribute selbst kein Verhalten haben und das Verhalten der Klasse, auf die sie angewendet werden, nicht ändern. Sie sind einfach zusätzliche Metadaten, die an eine Klasse angeheftet sind. Was ich als "Verhalten" empfand, war eigentlich nicht der Code der Attributklasse, sondern nur anderer Code wie die Serialize-Methode, die Metadaten interpretierte und bedingte Aktionen ausführte (z. B. eine Serialize-Methode mit Blick auf das Serializable-Attribut). – AaronLS

+0

Sehen Sie für gutes Beispiel von, wenn Attributkonstruktoren laufen: http://stackoverflow.com/a/1168590/84206 – AaronLS

Antwort

32

Ich habe keine Attribute in meiner täglichen Arbeit vor, aber ich habe über sie gelesen. Auch ich habe ein paar Tests gemacht, um zu bestätigen, was ich hier sagen werde. Wenn ich irgendwo falsch liege - zögern Sie nicht, mir das zu sagen :)

Soweit ich weiß, sind Attribute nicht wie normale Klassen. Sie werden nicht instanziiert, wenn Sie ein Objekt erstellen, auf das sie angewendet werden, und nicht eine statische Instanz, nicht 1 pro Instanz des Objekts. Sie greifen auch nicht auf die Klasse zu, auf die sie angewendet werden.

Stattdessen verhalten sie sich wie Eigenschaften (Attribute?: P) der Klasse. Nicht wie die .NET Klasse Eigenschaften, eher wie in der "eine Eigenschaft von Glas ist Transparenz" Art der Eigenschaft.Sie können überprüfen, welche Attribute auf eine Klasse angewendet werden, und dann entsprechend reagieren. Sie sind im Wesentlichen Metadaten, die an die Klassendefinition angehängt werden, nicht die Objekte dieses Typs.

Sie können versuchen, die Liste der Attribute für eine Klasse, Methode, Eigenschaft, etc etc .. Wenn Sie die Liste dieser Attribute erhalten - das ist, wo sie instanziiert werden. Dann können Sie auf die Daten innerhalb dieser Attribute einwirken.

z. In den Linq-Tabellen haben Eigenschaften Attribute, die definieren, auf welche Tabelle/Spalte sie sich beziehen. Diese Klassen verwenden diese Attribute jedoch nicht. Stattdessen überprüft der DataContext die Attribute dieser Objekte, wenn linq-Ausdrucksbäume in SQL-Code konvertiert werden.

Jetzt für einige echte Beispiele .. Ich habe diese in LinqPad ausgeführt, also mach dir keine Sorgen über die seltsame Dump() -Methode. Ich habe es mit Console.WriteLine ersetzt den Code leichter zu machen für die Menschen zu verstehen, die darüber nicht wissen :)

void Main() 
{ 
    Console.WriteLine("before class constructor"); 
    var test = new TestClass(); 
    Console.WriteLine("after class constructor"); 

    var attrs = Attribute.GetCustomAttributes(test.GetType()).Dump(); 
    foreach(var attr in attrs) 
     if (attr is TestClassAttribute) 
      Console.WriteLine(attr.ToString()); 
} 

public class TestClassAttribute : Attribute 
{ 
    public TestClassAttribute() 
    { 
     DefaultDescription = "hello"; 
     Console.WriteLine("I am here. I'm the attribute constructor!"); 
    } 
    public String CustomDescription {get;set;} 
    public String DefaultDescription{get;set;} 

    public override String ToString() 
    { 
     return String.Format("Custom: {0}; Default: {1}", CustomDescription, DefaultDescription); 
    } 
} 

[Serializable] 
[TestClass(CustomDescription="custm")] 
public class TestClass 
{ 
    public int Foo {get;set;} 
} 

Die Konsole Ergebnis dieser Methode ist:

before class constructor 
after class constructor 
I am here. I'm the attribute constructor! 
Custom: custm; Default: hello 

und die Attribute.GetCustomAttributes(test.GetType()) gibt dieses Array: (die Tabelle zeigt alle verfügbaren Spalten für alle Einträge .. Also nein, das Attribut Serializable haben diese Eigenschaften nicht :)) LinqPad Attributes Array

jede mor Got Die Fragen? Fragen sind willkommen!

UPD: Ich habe gesehen, dass Sie eine Frage stellen: warum sie verwenden? Als Beispiel erzähle ich Ihnen über die XML-RPC.NET-Bibliothek. Sie erstellen Ihre XML-RPC-Serviceklasse mit Methoden, die die xml-rpc-Methoden darstellen. Die Hauptsache ist gerade: In XmlRpc können die Methodennamen einige Sonderzeichen haben, wie Punkte. Sie können also eine flexlabs.ProcessTask() xml rpc-Methode verwenden.

Sie würden diese Klasse wie folgt definieren:

[XmlRpcMethod("flexlabs.ProcessTask")] 
public int ProcessTask_MyCustomName_BecauseILikeIt(); 

Das bin ich die Methode in der Art und Weise zu nennen erlaubt es mir gefällt, während immer noch die Öffentlichkeit Namen, wie es sein muss.

+0

sehr nette Antwort. – Dhananjay

6

Ja, sie werden mit den Parametern instanziiert, die Sie angeben.

Das Attribut "greift" nicht auf die Klasse zu. Das Attribut wird an die Attributliste der Klasse/Eigenschaft in den Reflektionsdaten angehängt.

[Serializable] 
public class MyFancyClass 
{ ... } 

// Somewhere Else: 

public void function() 
{ 
    Type t = typeof(MyFancyClass); 
    var attributes = t.GetCustomAttributes(true); 

    if (attributes.Count(p => p is SerializableAttribute) > 0) 
    { 
     // This class is serializable, let's do something with it! 

    }  
} 
+1

Ich bitte dieses nicht, schwierig zu sein, aber, um mein Verständnis zu verbessern: Was wäre der Vorteil eines Attributes, wenn es fungiert nur als Flagge? Andere Designs könnten dies tun, etwa wenn MyFancyClass eine IsSerialiable-Schnittstelle oder eine Schnittstelle mit einer IsSerializable-Eigenschaft implementiert. Wh ist ein Attribut notwendig? Welche Rolle wird von der SerializableAttribute-Klasse selbst, dem Code in dieser Attributklasse selbst und den Daten/Eigenschaften in dieser Klasse eingenommen? – AaronLS

+1

@AaronLS können Sie Attribute zur Laufzeit hinzufügen. Sie können sie auch behalten, wenn Sie Objekte in Dateien lesen/schreiben, selbst wenn der Code nicht weiß, was er damit machen soll. – phkahler

1

Es ist nicht viel Zeit, Ihnen eine ausführlichere Antwort zu geben, aber Sie können die Attribute finden, die mit Reflection auf einen Wert angewendet wurden. Wie beim Erstellen, erben Sie von der Attributklasse und arbeiten von dort - und die Werte, die Sie mit einem Attribut angeben, werden an den Konstruktor der Attributklasse übergeben.

Es ist schon eine Weile, wie Sie ...

Martin

16

Attribute sind im Wesentlichen Metadaten zu sagen, vielleicht in der Lage, die verschiedenen Teile des Codes angebracht werden kann. Diese Metadaten können dann interagieren und das Verhalten bestimmter Operationen beeinflussen.

Attribute können auf fast jeden Aspekt Ihres Codes angewendet werden. Beispielsweise können Attribute auf Assembly-Ebene wie die Attribute AssemblyVersion und AssemblyFileVersion zugeordnet werden, die die mit der Assembly verknüpften Versionsnummern steuern.

[assembly: AssemblyVersion("1.0.0.0")] 
[assembly: AssemblyFileVersion("1.0.0.0")] 

Dann kann das Attribut Serializable beispielsweise auf eine Typdeklaration angewendet werden, um den Typ als unterstützende Serialisierung zu kennzeichnen. Tatsächlich hat dieses Attribut eine besondere Bedeutung innerhalb der CLR und wird tatsächlich als spezielle Direktive direkt auf dem Typ in der IL gespeichert, dies wird optimiert, um als ein Bit-Flag gespeichert zu werden, das viel effizienter verarbeitet werden kann, da einige Attribute auf sind diese Art, die als Pseudo benutzerdefinierte Attribute bekannt sind.

Noch weitere Attribute können Methoden, Eigenschaften, Felder, Aufzählungen, die Rückgabewerte angewandt werden etc. Sie eine Vorstellung von den möglichen Zielen bekommen kann ein Attribut zu http://msdn.microsoft.com/en-us/library/system.attributetargets(VS.90).aspx unter diesem Link, indem Sie angewendet werden kann

Weitere Dazu können Sie eigene benutzerdefinierte Attribute definieren, die dann auf die entsprechenden Ziele angewendet werden, für die Ihre Attribute bestimmt sind. Zur Laufzeit kann Ihr Code die in den benutzerdefinierten Attributen enthaltenen Werte widerspiegeln und entsprechende Maßnahmen ergreifen.Für ein eher naives Beispiel, und dies nur aus Gründen des Beispiels :) Sie könnten eine Persistenz-Engine schreiben, die Klassen automatisch Tabellen in Ihrer Datenbank zuordnet und die Eigenschaften der Klasse zu Tabellenspalten mappt . Sie könnten mit der Definition zwei benutzerdefinierte starten

TableMappingAttribute 
ColumnMappingAttribute 

Attribute, die Sie dann auf Ihre Klassen anwenden können, als Beispiel wir

[TableMapping("People")] 
public class Person 
{ 
    [ColumnMapping("fname")] 
    public string FirstName {get; set;} 

    [ColumnMapping("lname")] 
    public string LastName {get; set;} 
} 

Wenn diese kompiliert, außer der Tatsache, dass der Compiler eine Person Klasse gibt die zusätzlichen Metadaten aus, die durch die benutzerdefinierten Attribute definiert sind. Sie können jedoch jetzt einen PersistanceManager schreiben, der die Attribute einer Instanz der Person-Klasse dynamisch untersuchen und die Daten in die People-Tabelle einfügen kann. Dabei werden die Daten in der FirstName-Eigenschaft der fname-Spalte und die LastName-Eigenschaft der lname-Spalte zugeordnet.

In Bezug auf Ihre Frage bezüglich der Instanzen der Attribute wird die Instanz des Attributs nicht für jede Instanz Ihrer Klasse erstellt. Alle Instanzen von Personen teilen dieselbe Instanz von TableMappingAttribute und ColumnMappingAttributes. Tatsächlich werden die Attributinstanzen nur erstellt, wenn Sie die Attribute zum ersten Mal tatsächlich abfragen.

+0

@Chris +1 Ich dachte, dass Attribute, die von der Serialisierung verwendet werden, und ORMs wie in Ihrem Beispiel etwas sehr Magisches zur Kompilierzeit machen, um das Objekt zu analysieren, damit es später während der Laufzeit lesen/schreiben kann Objekte zu/von Binär-/XML-Dateien (Serialisierung) oder Datenbanken (ORM). Ich dachte, die Attribute hätten vielleicht eine Art "Kompilierungszeitcode". Ich habe mich lange gefragt, welche coolen Dinge ich mit Attributen machen könnte. Nachdem ich mich damit befasst hatte, wurde mir klar, dass meine Wahrnehmung sehr weit von der Wahrheit entfernt war, aber ich hatte nie herausgefunden, wie Attribute benutzerdefinierte "Verhaltensweisen" lieferten. – AaronLS

6

Denken Sie an Attribute sind Post-its, die an die Klassen- oder Methodendefinitionen angehängt sind (eingebettet in die Assemblymetadaten).

Sie können dann ein Prozessor/Runner/Inspector-Modul haben, das diese Typen durch Nachdenken akzeptiert, nach diesen Post-its sucht und sie anders behandelt. Dies wird als deklarative Programmierung bezeichnet. Sie deklarieren etwas Verhalten, statt Code für sie im Typ zu schreiben.

  • Serializable Attribut für einen Typ deklariert, dass es serialisiert wird. Der XmlSerializer kann dann ein Objekt dieser Klasse akzeptieren und das Notwendige tun. Sie markieren die Methoden, die mit den richtigen Post-its serialisiert/ausgeblendet werden müssen.
  • ein anderes Beispiel wäre die NUnit. Der NUnit-Runner betrachtet die [TestFixture] -Attribute aller Klassen, die in der Zielbaugruppe definiert sind, um Testklassen zu identifizieren. Es sucht dann nach Methoden, die mit dem Attribut [Test] gekennzeichnet sind, um die Tests zu identifizieren, die dann ausgeführt werden und die Ergebnisse anzeigen.
  • Sie können durch this tutorial at MSDN laufen, die die meisten Ihrer Fragen zusammen mit einem Beispiel am Ende beantwortet hat. Obwohl sie eine Methode namens extrahiert haben könnten, anstatt diesen Code zu duplizieren. Das Beispiel "druckt Informationen" durch das Untersuchen von Attributen. Sie können jedoch alles in derselben Art und Weise tun.

    2

    Wenn Sie diesen herunterladbaren Open-Source-Code LINQ to Active Directory (CodePlex) auschecken, könnten Sie den Mechanismus der Datei Attributes.cs interessant finden, in der Bart De Smet alle seine Attributklassendefinitionen geschrieben hat. Ich habe dort Attribute gelernt.

    Kurz gesagt, können Sie spezialisieren die Attribute Klasse und Code einige spezialisierte Eigenschaften für Ihre Bedürfnisse.

    public class MyOwnAttributeClass : Attribute { 
        public MyOwnAttributeClass() { 
        } 
        public MyOwnAttributeClass(string myName) { 
         MyName = myName; 
        } 
        public string MyName { get; set; } 
    } 
    

    und dann können Sie es überall verwenden, wo MyOwnAttributeClass nützlich wird. Es kann entweder über eine Klassendefinition oder eine Eigenschaftendefinition hinausgehen.

    [MyOwnAttributeClass("MyCustomerName")] 
    public class Customer { 
        [MyOwnAttributeClass("MyCustomerNameProperty")] 
        public string CustomerName { get; set; } 
    } 
    

    Dann können Sie es durch Reflexion wie so erhalten:

    Attribute[] attributes = typeof(Customer).GetCustomAttribute(typeof(MyOwnAttributeClass)); 
    

    Bedenken Sie, dass das Attribut, das Sie in eckigen Klammern setzen immer der Konstruktor Ihrer Attribut ist. Wenn Sie also ein parametrisiertes Attribut haben wollen, müssen Sie Ihren Konstruktor als solchen codieren.

    Dieser Code wird zur Verfügung gestellt und kann nicht kompiliert werden. Sein Zweck ist es, Ihnen eine Idee zu geben, wie es funktioniert.

    Eigentlich möchten Sie eine andere Attributklasse für eine Klasse als für eine Eigenschaft haben.

    Hoffe, das hilft!