C# Basics Teil 2

Auch im zweiten Teil der C# Basics, gehe ich nur auf die Spezialitäten ein.

Flag-Enumerations

Enumerationen können auch benutzt werden, um Kombinationen daraus zu erstellen. Typischerweise sind die Werte immer von der Basis 2. Diese sollten mit dem Attribute „Flag“ gekenntzeichnet werden. Dies erhört auf der einen Seite das Verständnis, welche Enums kombiniert werden dürfen und die ToString() Methode kann (und wird) darauf reagieren.

Beispiel:

[Flags]
public enum Sides { Left = 1, Right = 2, Top = 4, Bottom = 8, TopLeft = Left | Top }

Generic Methods

Generische Klassen sind nun schon länger bekannt. Die generischen Methoden werden vielmals vergessen und nur wenig verwendet. Das folgende Beispiel zeigt eine generische Methode, die zwei Objekte eines beliebigen Types vertauscht. Der Vorteil dabei ist, dass die Methode nur typisiert verwendet werden kann, was vom Compiler überwacht wird.

Beispiel:

static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}

// Der Aufgruf der Methode wiefolgt
int a = 10;
int b = 20;
Swap<int>(ref a, ref b);

Generic Default Value

Das default() Schlüsselwort kann benutzt werden, um den Default-Value eines generischen Argumentes zu bestimmen. Ist das generische Argument ein Referenz-Type, wird der default immer null sein. Handelt es sich um einen Value-Type, ist der Default-Value das Resultat des bitweisen nullen der Struktur (dies hat damit zu tun, wie Strukturen in C# behandelt werden).

static void SetToDefault<T>(List<T> list)
{
for (int i = 0; i < list.Count; i++)
list[i] = default(T);
}

Kovarianz bei Generics

Generische Typen sind nicht kovariant. Dies bedeutet, dass auch wenn ein Typ B nach A gecastet werden könnte ein Generic T<B> nicht nach T<A> gecastet werden kann. Nicht so bei den Arrays. Ein Typ B[] kann in einen Typen A[] gecastet werden. Dieser Zustand bei den Generics kann die Wiederverwendbarkeit ziemlich einschränken.

Beispiel:

class A {}
class B : A {}
A[] a1 = new B[10];  // funktioniert!
List<A> a2 = new List<B>();   // kompiler-error

Möchte man nun eine Methode implementieren, die eine Liste von A’s und B’s verarbeiten kann, käme man zuerst auf folgende Definition

static void Foo(List<A> a) { }

Doch weil List<A> ein Generic ist und nicht kompatibel zu List<B> würde ein Aufruf Foo(new List<B>()); nicht funktionieren. Hier schafft .NET Abhilfe durch ein spezielles Konstruct. Es definiert eine generische Methode mit einem where-Constraint, damit nur A und Ableitungen davon benutzt werden können. Danach funktioniert der Aufruf.

static void Foo<T>(List<T> t) where T : A { }