Suchen und Finden
Generics
C# verfügt über zwei separate Mechanismen, um Code zu schreiben, der mit verschiedenen Typen verwendbar ist: Vererbung und Generics. Während bei der Vererbung die Wiederverwendbarkeit durch einen Basistyp ausgedrückt wird, geschieht dies bei Generics durch ein »Template«, das Typen als »Platzhalter« enthält. Generics können im Vergleich zur Vererbung die Typsicherheit erhöhen und für weniger Casting und Boxing sorgen.
Generische Typen
Ein generischer Typ deklariert Typparameter – Platzhaltertypen, die vom Anwender des generischen Typs gefüllt werden, indem er die Typargumente bereitstellt. Hier wurde ein generischer Typ Stack
{
int position;
T[] data = new T[100];
public void Push (T obj) => data[position++] = obj;
public T Pop() => data[--position];
}
Wir können Stack
stack.Push(5);
stack.Push(10);
int x = stack.Pop(); // x ist 10
int y = stack.Pop(); // y ist 5
Beachten Sie, dass in den beiden letzten Zeilen keine Downcasts erforderlich sind, was das Risiko eines Laufzeitfehlers und den Overhead der Boxing/Unboxing-Operationen vermeidet. Das macht unseren generischen Stack einem nichtgenerischen Stack überlegen, der object anstelle von T nutzt (ein Beispiel finden Sie unter »Der Typ object« auf Seite 87).
Stack
{
int position;
int[] data;
public void Push (int obj) => data[position++] = obj;
public int Pop() => data[--position];
}
Technisch ausgedrückt, ist Stack
Generische Methoden
Eine generische Methode deklariert Typparameter innerhalb ihrer Signatur. Mit generischen Methoden können viele grundlegende Algorithmen sehr allgemein implementiert werden. Hier sehen Sie eine generische Methode, die zwei Werte eines beliebigen Typs T vertauscht:
static void Swap{
T temp = a; a = b; b = temp;
}
Swap
Swap (ref x, ref y);
Im Allgemeinen ist es nicht notwendig, Typargumente an eine generische Methode zu übergeben, da der Compiler den Typ implizit ermitteln kann. Würde das zu einer Mehrdeutigkeit führen, können generische Methoden mit dem Typargument aufgerufen werden:
SwapInnerhalb eines generischen Typs ist eine Methode so lange nichtgenerisch, wie sie nicht selbst Typparameter einführt (mit den spitzen Klammern). In unserem generischen Stack nutzt die Methode Pop nur den schon im Typ bestehenden Typparameter T und ist daher nicht als generische Methode klassifiziert.
Methoden und Typen sind die einzigen Konstrukte, die Typparameter einführen können. Eigenschaften, Indexer, Events, Felder, Operatoren und so weiter können keine Typparameter deklarieren, auch wenn sie die Typparameter verwenden können, die vom umhüllenden Typ deklariert wurden. In unserem Beispiel mit dem generischen Stack könnten wir zum Beispiel einen Indexer schreiben, der ein generisches Element zurückgibt:
public T this [int index] { get { return data[index]; } }Auch Konstruktoren können nur die vorhandenen Typparameter nutzt, aber nicht selbst welche einführen.
Deklarieren generischer Parameter
Typparameter können bei der Deklaration von Klassen, Structs, Interfaces, Delegates (siehe »Delegates« auf Seite 110) und Methoden eingeführt werden. Ein generischer Typ bzw. eine generische Methode kann mehrere Typparameter haben:
class DictionaryDie Instanziierung erfolgt folgendermaßen:
var myDic = new DictionaryGenerische Typnamen und Methodennamen können überladen werden, solange sich die Anzahl der Typparameter unterscheidet. So kommen zum Beispiel die folgenden drei Typnamen nicht miteinander in Konflikt:
class A {}class A
class A
Es ist üblich, bei generischen Typen und Methoden mit einem einzelnen Typparameter diesen Parameter T zu nennen, solange der Sinn des Parameters klar ist. Bei mehreren Typparametern beginnt jeder Parameter mit T, hat aber einen stärker beschreibenden Namen.
typeof und ungebundene generische Typen
Zur Laufzeit gibt es keine offenen generischen Typen: Offene generische Typen werden bei der Kompilation geschlossen. Aber es kann zur Laufzeit ungebundene generische Typen geben – nur als Type-Objekt. Einen ungebundenen generischen Typ können Sie in C# nur mit dem typeof-Operator angeben:
class Aclass A
...
Type a1 = typeof (A<>); // Ungebundener Typ
Type a2 = typeof (A<,>); // Zeigt 2 Typargumente an
Console.Write (a2.GetGenericArguments().Count()); // 2
Sie können den typeof-Operator nutzen, um einen geschlossenen Typ anzugeben
Type a3 = typeof (Aoder einen offenen Typ (der zur Laufzeit geschlossen ist):
class BDer generische Wert default
Das Schlüsselwort default kann genutzt werden, um den Standardwert für einen angegebenen generischen Typparameter zu erhalten. Der Standardwert für einen Referenztyp ist null, der für Werttypen ist das Ergebnis eines bitweisen Löschens der Felder des Typs:
static void Zap{
for (int i = 0; i < array.Length; i++)
array[i] = default(T);
}
Generische Constraints
Standardmäßig kann ein Typparameter durch jeden beliebigen Typ ersetzt werden. Constraints können auf einen Typparameter angewandt werden, um spezifischere Typargumente zu verlangen. Es gibt sechs Arten von Constraints:
where T : Basisklasse // Basisklassen-Constraintwhere T : Interface // Interface-Constraint
where T : class // Referenztyp-Constraint
where T : struct // Werttyp-Constraint
where T : new() // Parameterloser Konstruktor
// Constraint
where U : T // Typ-Constraint
Im folgenden Beispiel verlangt GenericClass
interface Interface1 {}
class GenericClass
where U : new()
{ ... }
Constraints können überall dort angewendet werden, wo generische Parameter definiert sind, sowohl in Methoden als auch in Typdefinitionen.
Ein Basisklassen-Constraint verlangt, dass der Typparameter eine Subklasse einer bestimmten Klasse sein muss (oder die Klasse selbst); ein Interface-Constraint fordert, dass der Typparameter dieses Interface implementieren muss. Diese Constraints gestatten es, dass Instanzen des Typparameters implizit in diese Klasse oder dieses Interface umgewandelt werden.
Der class-Constraint und der struct-Constraint erfordern, dass T ein Referenztyp beziehungsweise ein (nicht nullbarer) Werttyp ist. Der parameterlose Konstruktor-Constraint erfordert, dass T einen...
Alle Preise verstehen sich inklusive der gesetzlichen MwSt.