ein Projekt von goloroden.de

Attribute

Was sind Attribute?

Wie bereits im Rahmen der Fehlerbehandlung erwähnt, gibt es Aspekte in Anwendungen, die über die reine fachliche Domäne hinausgehen. Als Beispiele waren dort unter anderem Sicherheit, Ausführungsgeschwindigkeit und Stabilität genannt. Auch die Fehlerbehandlung zählt zu diesen nicht-fachlichen Aspekten.

Neben Aspekten, die per Code definiert werden, bietet C# auch die Möglichkeit, Aspekte deklarativ umzusetzen, das heißt, ohne dass Code zu ihrer Umsetzung geschrieben werden müsste. Statt dessen werden die entsprechenden Stellen innerhalb der Anwendung mit sogenannten Attributen markiert, die Einfluss auf die Semantik des markierten Codes haben.

Beispielsweise gibt es für Enumerationen ein Attribut, das bewirkt, dass die interne Abbildung der Enumeration auf Ganzzahlen dem Schema der Zweierpotenzen folgt, statt die Zahlen lediglich fortlaufend zuzuordnen. Dieses Attribut ist beispielsweise dann äußerst nützlich, wenn die einzelnen Werte einer Enumeration binär verknüpft werden sollen.

Um ein Attribut in C# zu verwenden, genügt es, das entsprechende Attribut vor dem zu markierenden Abschnitt innerhalb eckiger Klammern anzugeben. Das Attribut, um die einer Enumeration zugeordneten Zahlen als Zweierpotenzen zu organisieren, heißt FlagsAttribute und befindet sich im Namensraum System.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Contains colors.
    /// </summary>
    [Flags]
    enum Colors
    {
        /// <summary>
        /// Represents the red color.
        /// </summary>
        Red,    // = 1

        /// <summary>
        /// Represents the green color.
        /// </summary>
        Green,  // = 2

        /// <summary>
        /// Represents the blue color.
        /// </summary>
        Blue    // = 4
    }
}
Wie das Beispiel zeigt, entfällt bei der Angabe eines Attributs das Suffix Attribute, obwohl der interne Bezeichner der Klasse FlagsAttribute lautet. Attribute ermöglichen prinzipiell also, die Semantik von Code auf deklarativem Wege zu verändern.

Die meisten Attribute ermöglichen außerdem, sie mit Hilfe von Parametern an den jeweiligen Kontext anzupassen. Prinzipiell werden Parameter zu Attributen ähnlich denen zu einer Methode angegeben, innerhalb runder Klammern. Allerdings werden bei Attributen zwei Typen von Parametern unterschieden: Positions- und Namensparameter.

Während Positionsparameter eine feste Reihenfolge besitzen, in der sie angegeben werden müssen, ist diese bei Namensparametern frei wählbar. Allerdings muss diesen ein Name vorangestellt werden, damit C# den Parameter entsprechend zuordnen kann. Die meisten Attribute folgen dem Schema, dass Positionsparameter zwingende, Namensparameter allerdings nur optionale Parameter darstellen. Sofern Namensparameter angegeben werden, muss dies nach den Positionsparametern erfolgen.

Ein Beispiel für Positionsparameter bietet das Attribut ObsoleteAttribute, das genutzt werden kann, um Methoden oder Typen zu kennzeichnen, die aus Kompatibilitätsgründen noch enthalten sind, allerdings nicht mehr verwendet werden sollten. Es gibt dieses Attribut in drei Ausführungen: Ohne Parameter, mit einem und mit zwei Parametern. Der erste Parameter definiert eine Fehlermeldung, die C# ausgeben soll, wenn die Methode oder der Typ verwendet wird, der zweite Parameter legt mit Hilfe eines logischen Wertes fest, ob der Compiler eine Warnung oder einen Fehler erzeugen soll.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a foo class.
    /// </summary>
    public class Foo
    {
        /// <summary>
        /// Does nothing.
        /// </summary>
        [Obsolete]
        public void Bar()
        {
        }

        /// <summary>
        /// Does nothing.
        /// </summary>
        /// <param name="param0">A foo parameter.</param>
        [Obsolete("Use method X instead.")]
        public void Bar(int param0)
        {
        }

        /// <summary>
        /// Does nothing.
        /// </summary>
        /// <param name="param0">A foo parameter.</param>
        /// <param name="param1">Another foo parameter.</param>
        [Obsolete("Use method X instead.", true)]
        public void Bar(int param0, int param1)
        {
        }
    }
}

Benutzerdefinierte Attribute

Außer den vordefinierten Attributen bietet C# auch die Möglichkeit, eigene Attribute zu definieren. Dies geschieht, indem eine eigene Klasse definiert wird, die von der Basisklasse Attribute im Namensraum System ableitet und deren Name auf das Suffix Attribute endet.
C#
1
2
3
4
5
6
7
8
9
10
11
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents the author attribute.
    /// </summary>
    public class AuthorAttribute : Attribute
    {
    }
}
Um dieses Attribut mit Parametern zu versehen, werden zum einen Felder benötigt, welche die entsprechenden Werte aufnehmen. Außerdem muss das Attribut für Positionsparameter mindestens mit einem Konstruktor versehen werden, für Namensparameter muss es entsprechende Eigenschaften geben.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents the author attribute.
    /// </summary>
    public class AuthorAttribute : Attribute
    {
        /// <summary>
        /// Contains the name.
        /// </summary>
        private string _name;

        /// <summary>
        /// Contains the email address.
        /// </summary>
        private string _eMail;

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get
            {
                return this._name;
            }

            set
            {
                this._name = value;
            }
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        /// <value>The email.</value>
        public string EMail
        {
            get
            {
                return this._eMail;
            }

            set
            {
                this._eMail = value;
            }
        }

        /// <summary>
        /// Initializes a new instance of the
        /// AuthorAttribute type.
        /// </summary>
        /// <param name="name">The name.</param>
        public AuthorAttribute(string name)
        {
            // Set the values.
            this._name = name;
        }
    }
}
In diesem Beispiel ist es auf Grund des Konstruktors notwendig, den Namen des Autors anzugeben, die E-Mail-Adresse ist allerdings optional. Methoden und Typen können, sofern sie mit diesem Attribut markiert werden, mit der Angabe versehen werden, wer sie entwickelt hat und für sie zuständig ist, was beispielsweise in Teams nützlich zu wissen sein kann.
C#
1
2
3
4
5
6
7
8
9
10
11
12
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a foo class.
    /// </summary>
    [Author("Golo Roden", EMail = "webmaster@goloroden.de")]
    public class Foo
    {
    }
}

Ziele von Attributen

Attribute selbst können wiederum mit Attributen versehen werden, was in C# unter anderem dafür genutzt wird, die potenziellen Ziele für Attribute vorzugeben. Ein Ziel ist ein Element innerhalb des Codes, auf welches das Attribut angewendet werden kann, wie beispielsweise eine Methode, ein Parameter oder eine Klasse.

Ziele werden in C# mit Hilfe des Attributes AttributeUsageAttribute definiert, das als Parameter eine bitweise-oder-verknüpfte Liste von Zielen erwartet. Um die Verwendung des Attributs AuthorAttribute beispielsweise auf Methoden und Klassen einzuschränken, werden dem AttributeUsageAttribut die entsprechenden Ziele übergeben.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents the author attribute.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method |
        AttributeTargets.Class)]
    public class AuthorAttribute : Attribute
    {
        /// <summary>
        /// Contains the name.
        /// </summary>
        private string _name;

        /// <summary>
        /// Contains the email.
        /// </summary>
        private string _eMail;

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get
            {
                return this._name;
            }

            set
            {
                this._name = value;
            }
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        /// <value>The email.</value>
        public string EMail
        {
            get
            {
                return this._eMail;
            }

            set
            {
                this._eMail = value;
            }
        }

        /// <summary>
        /// Initializes a new instance of the
        /// AuthorAttribute type.
        /// </summary>
        /// <param name="name">The name.</param>
        public AuthorAttribute(string name)
        {
            this._name = name;
        }
    }
}