Attribute und der System.Attribute Typ
Attribute werden genutzt, um Typen und Typen-Members (z.B. Felder, Eigenschaften, Methoden) zu "dekorieren". Attribute werden als Metadaten innerhalb einer .NET Framework Anwendung (oder Assembly) gesichert. Zur Laufzeit kann man Typen, Typen-Members und Instanzen auf das Vorkommen solcher Attribute hin überprüfen.
Die generelle Syntax für Delphi und C# ist im Grunde gleich und kommt direkt vor dem Symbol, auf welches das Attribut angewandt wird:
Code:
[AttributeTypeName(Parameter, Parameter)]
[b]public class [/b]Foo { ... }
Hinweis: Es ist definiert, dass Attributnamen immer auf das Wort
Attribute enden (z.B.: DesignerAttribute, ComVisibleAttribute). Die meisten Compiler erlauben es einem den Teil "Attribute" wegzulassen.
Was kann man nun mit diesen Attributen anfangen? Die meisten Entwicklungsumgebungen bieten spezielle
Design-Time Klassen, welche es einem ermöglichen eine Komponente/ein Control in der Oberfläche (zur Designzeit) zu modifizieren. Dazu dient (in .NET) zum Beispiel das
DesignerAttribute:
Code:
[DesignerAttribute("MyControlDesigner")]
[b]public class [/b]MyControl : System.Windows.Forms.Control
{ … }
Wenn der WinForms Designer einen Designer für ein Control erstellt, stößt dieser auf das [i]DesignerAttribute[i] der Klasse und erstellt einen
MyControllDesigner für diese.
Ein weiteres gutes Beispiel ist
ComVisibleAttribute. Man kann .NET Assemblies dem
COM Subsystem zur Verfügung stellen, indem man diese mit
regasm.exe registriert. Jeder Typ, welcher mit
ComVisibleAttribute markiert ist wird damit dem
COM Subsystem zugänglich:
Code:
[ComVisible(true)]
[b]public interface [/b]MyInterface
{
}
Man kann sich jetzt die Frage stellen, ob man jetzt jeden Typ so markieren muss, der dem
COM Subsystem zur Verfügung gestellt werden soll. Die Antwort ist: nein. Man kann dieses Attribut auch dem gesamten Assembly zur Verfügung stellen:
Code:
[assembly: ComVisible(true)]
Assembly-Level Attribute können auch für andere Dinge genutzt werden (z.B.: Version, Autor, Firmenname, etc.):
Code:
[assembly: AssemblyTitle("This is my cool assembly")]
[assembly: AssemblyDescription("It does neat stuff")]
[assembly: AssemblyCompany("Borland Software Corporation")]
[assembly: AssemblyVersion("1.0.3.5")]
Attribute abfragen
Um Attribute für einen Typen oder eine Instanz zu ermitteln kann man sich der
Reflection und des
TypeDescriptor bedienen:
Delphi-Quellcode:
var
Attributes: AttributeCollection;
begin
Attributes := TypeDescriptor.GetAttributes(Self);
Hinweis: Man kann eine Instanz (z.B: Self in Delphi oder this in C#) oder auch einen Sysmte.Type an
TypeDescriptor.GetAttributes übergeben. Im Hintergrund wird immer auf den Typ zugegriffen, da Attribute Metadaten sind und diese im Typ, nicht in der Instanz, gesichert sind.
Nachdem man Zugriff auf die
AttributeCollection erlangt hat, kann man diese recht einfach durchlaufen:
Delphi-Quellcode:
for AnAttribute in Attributes do
begin
TextBox1.Text := TextBox1.Text + AnAttribute.ToString + #13#10;
end;
Hinweis: Hier wurde die neuere "Delphi 9" for-Schleifen-Syntax genutzt. In Delphi 8 muss man den Iterator manuell erstellen und durchlaufen.
Wenn man nur auf ein bestimmtes Attribute zugreifen möchte, so kann man das wie folgend tun:
Delphi-Quellcode:
var
Attributes: AttributeCollection;
DefaultEventAttr: DefaultEventAttribute;
begin
...
DefaultEventAttr :=
DefaultEventAttribute(Attributes[TypeOf(DefaultEventAttribute)]);
if DefaultEventAttr <> nil then
Text := 'Has a Default Event name of: ' + DefaultEventAttr.Name;
Man beachte, dass
null zurückgeliefert wird, wenn der Typ keine Attribute besitzt.
Wenn man die Attribute eines bestimmten Mitgliedes (Type-Member) ermitteln möchte, muss man
Reflection nutzen, um das Mitlgied zu finden. Anschließend kann man
Attribute.GetCustomAttributes(..) nutzen, um auf die Attribute des Mitgliedes zuzugreifen:
Delphi-Quellcode:
[TMyCustomMethodAttriute]
procedure TMainForm.Button1_Click(sender: System.Object; e: System.EventArgs);
var
MyType: System.Type;
ClickMethod: MethodInfo;
MethodAttrs: array of Attribute;
MethodAttr: Attribute;
I: Integer;
begin
{ Use reflection to get the Button1_Click member}
MyType := GetType;
ClickMethod := MyType.GetMethod('Button1_Click', BindingFlags.NonPublic or BindingFlags.Instance);
MethodAttrs := Attribute.GetCustomAttributes(ClickMethod);
TextBox1.Text := '--- Method Attributes --- '#13#10;
for I := 0 to Length(MethodAttrs) - 1 do
begin
MethodAttr := MethodAttrs[I];
TextBox1.Text := TextBox1.Text + MethodAttr.ToString + #13#10;
end;
Wenn man
Attribute.GetCustomAttributes(..) nutzt, kann man einen zweiten Parameter übergeben, um auch die Vorgängertypen zu untersuchen. In diesem Beispiel würde es nichts ändern, da diese Methode keine Vorgänger hat.
Eigene Attribute
Es ist recht einfach eigene Attribute zu erstellen. Man muss nur wenige Dinge beachten. Am wichtigsten ist, das jedes Attribute direkt oder indirekt von
System.Attribute abgeleitet werden muss:
Delphi-Quellcode:
{ This can only be applied to methods }
[AttributeUsage(AttributeTargets.Method)]
TMyCustomMethodAttriute = class(Attribute)
end;
Hinweis: Bachten Sie, dass das
AttributeUsage Attribute dem neuen Attribute hinzugefügt wurde. Das teilt dem Compiler mit, dass dieses Attribute auch als solches genutzt werden darf.