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