Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 22 Attribute
  gp 22.1 Verwenden von Attributen
  gp 22.2 Einlegen von Attributen
  gp 22.3 Eigene Attribute

Kapitel 22 Attribute

In den meisten Programmiersprachen werden einige der Informationen durch Deklarationen ausgedrückt, andere über den Code. In der folgenden Klassenmitgliederdeklaration beispielsweise

public int Test;

reservieren Compiler und Laufzeitumgebung Speicherplatz für eine integer-Variable und legen deren Schutz so fest, dass diese überall sichtbar ist. Hierbei handelt es sich um ein Beispiel deklarativer Informationen; diese Methode ist sehr schön, da es sich um einen ökonomischen Ausdruck handelt, bei dem der Compiler die Details übernimmt.

Üblicherweise werden die Typen deklarativer Informationen durch den Sprachentwickler vorgegeben und können durch den Benutzer der Sprache nicht erweitert werden. Ein Benutzer, der ein bestimmtes Datenbankfeld mit einem Feld einer Klasse verknüpfen möchte, muss beispielsweise eine Methode finden, mit der in der jeweiligen Sprache eine solche Beziehung ausgedrückt wird, die Beziehung gespeichert wird und zur Laufzeit auf die Informationen zugegriffen werden kann. In einer Sprache wie C++ kann ein Makro definiert werden, das die Informationen in einem Feld speichert, das Bestandteil des Objekts ist. Solche Schemata funktionieren, sind aber fehleranfällig und nicht allgemein einsetzbar. Und sie sind hässlich.

Die .NET-Laufzeitumgebung unterstützt Attribute, bei denen es sich im Prinzip um Anmerkungen handelt, die für Elemente des Quellcodes gesetzt werden, beispielsweise für Klassen, Mitglieder, Parameter usw. Attribute können zur Änderung des Laufzeitverhaltens, zur Bereitstellung von Transaktionsinformationen zu einem Objekt oder zur Lieferung von strukturellen Informationen für einen Designer verwendet werden. Die Attributinformationen werden mit den Metadaten des Elements gespeichert und können zur Laufzeit über die Reflektion leicht abgerufen werden.

C# verwendet ein Bedingungsattribut, um zu steuern, welche Mitgliederfunktion aufgerufen wird. Eine Verwendung des Bedingungsattributs sähe folgendermaßen aus:

using System.Diagnostics;
class Test
{
    [Conditional("DEBUG")]
    public void Validate()
    {
    }
}

Die meisten Programmierer verwenden eher vordefinierte Attribute, als selbst Attributklassen zu schreiben.


Galileo Computing

22.1 Verwenden von Attributen  downtop

Angenommen, für ein Projekt müssen die Codeumarbeitungen verfolgt werden, die für Klassen vorgenommen werden, damit ermittelt werden kann, welche Codeänderungen abgeschlossen sind. Die Änderungsinformationen zum Code können in einer Datenbank gespeichert werden, damit der Status leicht abgefragt werden kann, oder die Informationen werden in Kommentaren gespeichert, wodurch die benötigten Informationen direkt im Code verfügbar sind.

Alternativ könnte jedoch auch ein Attribut eingesetzt werden, mit dem beide Zugriffsmöglichkeiten gegeben sind.

Hierzu wird eine Attributklasse benötigt. Eine Attributklasse definiert den Namen eines Attributs, die Art der Erstellung und die gespeicherten Informationen. Die Einzelheiten bei der Definition einer Attributklasse werden im Abschnitt »Eigene Attribute« besprochen.

Die Attributklasse sieht folgendermaßen aus:

using System;
[AttributeUsage(AttributeTargets.Class)]
public class CodeReviewAttribute: System.Attribute
{
    public CodeReviewAttribute(string reviewer, string date)
    {
        this.reviewer = reviewer;
        this.date = date;
    }
    public string Comment
    {
        get
        {
            return(comment);
        }
        set
        {
            comment = value;
        }
    }
    public string Date
    {
        get
        {
            return(date);
        }
    }
    public string Reviewer
    {
        get
        {
            return(reviewer);
        }
    }
    string reviewer;
    string date;
    string comment;
}
[CodeReview("Eric", "01-12-2000", Comment="Bitchin' Code")]
class Complex
{
}

Das AttributeUsage-Attribut vor der Klasse gibt an, dass dieses Attribut nur für Klassen verwendet werden kann. Wenn ein Attribut für ein Programmelement verwendet wird, prüft der Compiler, ob die Verwendung des Attributs für das jeweilige Programmelement zulässig ist.

Die Benennungskonvention für Attribute schreibt vor, dass Attribute an das Ende des Klassennamens gehängt werden muss. So kann leichter ermittelt werden, welche Klassen Attributklassen und welche Klassen normale Klassen darstellen. Alle Attribute müssen von System.Attribute abgeleitet werden.

Die Klasse definiert eine einzige Erstellungsroutine, die als Parameter den Prüfer und das Datum umfasst sowie über die öffentliche Zeichenfolge Comment verfügt.

Wenn der Compiler die Attributverwendung der Klasse Complex prüft, wird zunächst nach einer von Attribute abgeleiteten Klasse namens CodeReview gesucht. Diese ist nicht vorhanden, daher wird als Nächstes nach einer Klasse CodeReviewAttribute gesucht, die vorhanden ist.

Als Nächstes prüft der Compiler, ob das Attribut für die Klasse zulässig ist.

Anschließend wird überprüft, ob eine Erstellungsroutine vorliegt, die mit den in der Attributverwendung angegebenen Parametern übereinstimmt. Ist eine solche Erstellungsroutine vorhanden, wird eine Instanz des Objekts erstellt – die Erstellungsroutine wird mit den angegebenen Werten aufgerufen.

Sind benannte Parameter vorhanden, werden die Namen der Parameter mit einem Feld oder einer Eigenschaft in der Attributklasse abgeglichen, anschließend werden Feld oder Eigenschaft auf den angegebenen Wert gesetzt.

Im nächsten Schritt wird der aktuelle Status der Attributklasse in den Metadaten des entsprechenden Programmelements gespeichert.

Dies ist zumindest das, was logisch gesehen passiert. Tatsächlich sieht es jedoch nur so aus, als würde dies geschehen; siehe hierzu den gesondert formatierten Abschnitt »Einlegen von Attributen«.


Galileo Computing

22.1.1 Noch ein paar Details  downtop

Einige Attribute können nur einmal auf ein vorgegebenes Element angewendet werden. Andere, auch Mehrfachattribute genannt, können häufiger verwendet werden. Diese Eigenschaft kann beispielsweise dazu genutzt werden, verschiedene Sicherheitsattribute auf eine einzelne Klasse anzuwenden. Die Dokumentation zu einem Attribut beschreibt, ob dieses einmalig oder mehrfach verwendet werden kann.

In den meisten Fällen ist klar, dass sich das Attribut auf ein bestimmtes Programmelement bezieht. Betrachten Sie jedoch den folgenden Fall:

class Test
{
    [ReturnsHResult]
    public void Execute() {}
}

Normalerweise würde ein Attribut an dieser Position für die Mitgliederfunktion gelten, dieses Attribut bezieht sich jedoch auf den Rückgabetyp. Wie kann der Compiler dies erkennen?

Es gibt verschiedene Situationen, in denen dieser Fall auftreten kann:

gp  Methode kontra Rückgabewert
gp  Ereignis kontra Feld oder Eigenschaft
gp  Zuweisung kontra Rückgabewert
gp  Eigenschaft kontra Erstellungsroutine kontra Rückgabewert der Abrufroutine kontra Wertparameter der Festlegungsroutine

In jeder dieser Situationen hat sich einer der Fälle aufgrund seiner häufigeren Verwendung als Standardfall durchgesetzt. Zur Festlegung von Attributen für alle anderen Fälle muss das Element angegeben werden, auf das sich das Attribut bezieht.

class Test
{
    [return:ReturnsHResult]
    public void Execute() {}
}

Das return: gibt an, dass dieses Attribut auf den Rückgabewert angewendet werden soll.

Das Element kann auch dann angegeben werden, wenn keine Unklarheit besteht. Die Bezeichner lauten folgendermaßen:

Bezeichner Beschreibung
assembly Attribut gilt für Assemblierung
module Attribut gilt für Modul
type Attribut gilt für eine Klasse oder eine Struktur
method Attribut gilt für eine Methode
property Attribut gilt für eine Eigenschaft
event Attribut gilt für ein Ereignis
field Attribut gilt für ein Feld
param Attribut gilt für einen Parameter
return Attribut gilt für den Rückgabewert

Attribute, die auf Assemblierungen oder Module angewendet werden, müssen nach jeder using-Klausel und vor dem Code angegeben werden.

using System;
[assembly:CLSCompliant(true)]

class Test
{
    Test() {}
}

In diesem Beispiel wird das ClsCompliant-Attribut auf die gesamte Assemblierung angewendet. Alle Attribute auf Assemblierungsebene, die in einer Datei der Assemblierung deklariert werden, werden zusammen gruppiert und der Assemblierung angehängt.

Zur Verwendung eines vordefinierten Attributs ermitteln Sie zunächst die Erstellungsroutine, die am ehesten mit den bereitzustellenden Informationen übereinstimmt. Als Nächstes schreiben Sie das Attribut und übergeben die Parameter an die Erstellungsroutine. Abschließend verwenden Sie die Syntax für benannte Parameter, um zusätzliche Informationen zu übergeben, die nicht Teil der Erstellungsroutinenparameter sind.

Weitere Beispiele zur Attributverwendung finden Sie in Kapitel 29, Interoperabilität.


Galileo Computing

22.2 Einlegen von Attributen  downtop

Es gibt ein paar Gründe, warum das Erstellen von Attributen nicht wirklich wie beschrieben funktioniert, und die Gründe sind alle leistungsbezogen. Damit der Compiler das Attributobjekt auch tatsächlich erstellen kann, muss die .NET-Laufzeitumgebung ausgeführt werden, daher würde jede Kompilierung zu einem Umgebungsstart führen, d. h., jeder Compiler müsste als verwaltete ausführbare Datei ausgeführt werden.

Darüber hinaus ist die Objekterstellung nicht wirklich erforderlich, da die Informationen nur gespeichert (»eingelegt«) werden sollen.

Der Compiler stellt demnach sicher, dass er das Objekt erstellen könnte, ruft die Erstellungsroutine auf und setzt die Werte für benannte Parameter. Die Attributparameter werden anschließend in einem Chunk binärer Informationen abgelegt, die mit den Metadaten des Objekts verstaut werden.


Galileo Computing

22.3 Eigene Attribute  downtop

Bei der Definition von Attributklassen und deren Reflektion zur Laufzeit sind einige weitere Punkte zu berücksichtigen. In diesem Abschnitt werden einige Dinge besprochen, die beim Entwurf eines Attributs von Bedeutung sind.

Beim Schreiben von Attributen müssen zwei Dinge festgelegt werden. Hierzu zählen a) die Programmelemente, auf die das Attribut angewendet werden kann, und b) die Informationen, die mit dem Attribut gespeichert werden.


Galileo Computing

22.3.1 Attributverwendung  downtop

Durch Anwendung des AttributeUsage-Attributs auf eine Attributklasse wird gesteuert, wo das Attribut eingesetzt werden kann. Die möglichen Werte des Attributs werden in der Aufzählung AttributeTargets aufgelistet und lauten folgendermaßen:

Wert Bedeutung
Assembly Die Programmassemblierung
Module Die aktuelle Programmdatei
Class Eine Klasse
Struct Eine Struktur
Enum Ein Aufzählungsbezeichner
Constructor Eine Erstellungsroutine
Method Eine Methode (Mitgliedsfunktion)
Property Eine Eigenschaft
Field Ein Feld
Event Ein Ereignis
Interface Eine Schnittstelle
Parameter Ein Methodenparameter
Return Der Rückgabewert für die Methode
Delegate Eine Zuweisung
All Überall
ClassMembers Klasse, Struktur, Aufzählungsbezeichner, Erstellungsroutine, Methode, Eigenschaft, Feld, Ereignis, Zuweisung, Schnittstelle

Als Teil des Attributs AttributeUsage kann einer dieser Werte angegeben werden oder per OR zu einer Liste zusammengestellt werden.

Über das Attribut AttributeUsage wird auch festgelegt, ob ein Attribut einmalig oder mehrfach eingesetzt werden kann. Dies wird mit dem benannten Parameter AllowMultiple erreicht. Ein solches Attribut würde wie folgt aussehen:

[AttributeUsage(    AttributeTargets.Method | AttributeTargets.Event,
                AllowMultiple = true)]

Galileo Computing

22.3.2 Attributparameter  toptop

Die vom Attribut gespeicherten Informationen sollten in zwei Gruppen unterteilt werden: die Informationen, die bei jeder Verwendung erforderlich sind, und die optionalen Informationen.

Die bei jeder Verwendung erforderlichen Informationen sollten über die Erstellungsroutine für die Attributklasse abgerufen werden. Dies zwingt den Benutzer, bei der Attributverwendung alle Parameter anzugeben.

Optionale Elemente sollten als benannte Parameter implementiert werden, was dem Benutzer das Angeben der optionalen Elemente bei Bedarf ermöglicht.

Kann ein Attribut anhand verschiedener Methoden erstellt werden, die jeweils unterschiedliche Informationen erfordern, können für jede Verwendung separate Erstellungsroutinen deklariert werden. Verwenden Sie separate Erstellungsroutinen nicht als Alternative zu optionalen Elementen.

Attributparametertypen

Das Attributspeicherformat unterstützt nur einen Teilsatz aller .NET-Laufzeitumgebung-Typen, daher können nur einige Typen als Attributparameter eingesetzt werden. Folgende Typen sind erlaubt:

gp  bool, byte, char, double, float, int, long, short, string
gp  object
gp  System.Type
gp  Eine Aufzählung (enum) mit öffentlichem Zugriff (keine Verschachtelung in nicht öffentlichen Abschnitten)
gp  Ein eindimensionales Array der vorstehenden Typen.
   

Select * from SQL Server 2000




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de