.NET Attribute und Reflection
.NET bietet einen Mechanismus an Assembly-, Klassen-, Methoden-, Felder- oder Propertiesdefinitionen Metadaten anzuhängen – sogenannten Attribute.
Wir sehen uns nun an, wie man eigene Attribute definiert und sie mittel Reflection wieder ausliest.
Attribut Deklaration
Ein Attribute, dass an eine Methode gebunden ist:
class Test
{
[MyAttribute]
public void helloWorld ()
{
}
}
Custom-Attribute
Eigene Attribute werden immer als Unterklasse von ”’System.Attribute”’ deklariert. Zusätzlich wird über Attribute (sogennante Meta-Attribute) festgelegt in welchem Kontext das Attribute erlaubt ist. Attributklassen müssen ”’public”’ sein.
Die Klasse CodeInfoAttribute
Wir wollen nun ein selbstdefiniertes Attribut ”’CodeInfo”’ erstellen, dass Informationen wie Author oder Editierungsdatum enthält.
namespace Net.Experimentalworks.Attributes
{
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Method |
AttributeTargets.Struct,
Inherited = false,
AllowMultiple = true)]
class CodeInfoAttribute : System.Attribute
{
}
}
Die Namensgebung ist ein vom Compiler unterstützt Übereinkunft, dass alle Attribute mit *Attribute enden. Man kann später sowohl mit [CodeInfoAttribute] als auch mit [CodeInfo] auf das Attribut zugreifen.
AttributUsage
Das Meta-Attribut AttributeUsage gibt an, in welchem Kontext das Attribut gültigt ist.
AttributeTarget
Möglichkeiten:
- AttributeTarget.All
- AttributeTarget.Assembly
- AttributeTarget.Class
- AttributeTarget.Constructor
- AttributeTarget.Delegate
- AttributeTarget.Enum
- AttributeTarget.Event
- AttributeTarget.Field
- AttributeTarget.Interface
- AttributeTarget.Method
- AttributeTarget.Module
- AttributeTarget.Parameter
- AttributeTarget.Property
- AttributeTarget.ReturnValue
- AttributeTarget.Struct
Inherited
Gibt an, ob ein in einer Basisklasse verwendetes Attribut auf die Unterklassen vererbt wird.
AllowMultiple
Der Boolean-Parameter AllowMultiple gibt an, ob ein Attribut mehrfach im selbsten Kontext vorkommen darf.
Beispiel
[AttributeUsage(AttributeTarget.All,Inherited=true,AllowMultiple=true)]
Parametisierung von Custom-Attributen
Jedes Attribut kann zusätzliche Parameter beim Aufruf erhalten. Man unterscheidet zwischen Named-Parameters und Position-Parameters.
Position-Parameters
Position-Parametisierung ähnelt dem Aufruf eines Konstruktors und wird über diesen implementiert.
[CodeInfo("AuthorName")]
public void hello()
{
}
Die Erweiterung der Klasse CodeInfo
Um die Beschreibung, die bei der Verwendung des Attributes angegeben werden kann zu unterstützen können,
benötigen wir einen Konstruktor, der den Description-Parameter übernimmt.
namespace Net.Experimentalworks.Attributes
{
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Method |
AttributeTargets.Struct,
Inherited = false,
AllowMultiple = true)]
class CodeInfoAttribute : System.Attribute
{
private string author;
private string date;
// Wir unterstüzen weiterhin eine parameterlose Initialisierung
public CodeInfoAttribute(string Author)
{
author = Author;
}public CodeInfoAttribute(string Author, string Date) : this(Author)
{
date = Date;
}
}
}
Unser Attribut kann also mit oder ohne Parameter initialisiert werden:
[CodeInfo("dsp")]
[CodeInfo("dsp", "03.09.2006")]
Named-Parameters
[CodeInfo(Date="03.09.2006", Author="dsp")]
Die Erweiterung der Klasse ConfiguratableAttribute
Named-Parameters werden über Properties bzw. Fields implementiert. Für jeden Namend-Parameter muss eine öffentlich lesbare Poperty oder ein Feld mit demselben Namen existieren.
namespace Net.Experimentalworks.Attributes
{
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Method |
AttributeTargets.Struct,
Inherited = false,
AllowMultiple = true)]
class CodeInfoAttribute : System.Attribute
{
// Als Feld:
public string Author;// Als Poperty:
private DateTime date;
public string Date
{
get {
return date.ToString();
}
set {
date = DateTime.Parse(value);
}
}
}
}
Mischformen
Natürlich sind auch Mischformen möglich.
using System;
namespace Net.Experimentalworks.Attributes
{
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Method |
AttributeTargets.Struct,
Inherited = false,
AllowMultiple = true)]
class CodeInfoAttribute : System.Attribute
{
public string Author;
public string Date;
public CodeInfoAttribute(string author)
{
Author = author;
}public CodeInfoAttribute(string author, string date) : this(author)
{
Date = date;
}
}
}
Zugriff über Reflection
Das Attribut lässt sich über Reflection von .NET auslesen. Reflection ist ein Mechanismus der es erlaubt während der Laufzeit auf Klassendeklarationen und Metadaten zuzugreifen.
Beispiel
using System;
using System.Reflection;
namespace Net.Experimentalworks.Attributes.Inspection
{
public class Inspector
{
public void printAllCodeInfoMethods (string ClassName)
{
foreach(MemberInfo mi in Type.GetType(ClassName).GetMembers())
{
foreach (Object att in ((MemberInfo) mi).GetCustomAttributes(true))
{
if (att.GetType().Name == “CodeInfoAttribute”)
{
Console.WriteLine(“Type: {0} Name: {1} Author: {2}”,
mi.MemberType, mi.Name, ((CodeInfoAttribute)att).Date);
}
}
}
}
}