.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);
}
}
}
}
}

Leave a Reply