9.2 Bereitstellen einer generischen Klasse
Generics erlauben die Verwendung von Datentypen, die man zum Zeitpunkt der Entwicklung noch nicht festlegen kann oder will. Dazu wird anstelle eines konkreten Datentyps in der Klassendefinition ein Platzhalter angegeben, der innerhalb von spitzen Klammern steht. Dabei hat sich das Verwenden von Buchstaben eingebürgert. Am häufigsten trifft man auf das »T«, das für »Typ« steht. In der generischen Klasse kann der Platzhalter wie ein regulärer Datentyp verwendet werden.
Im folgenden Beispiel wird gezeigt, wie aus der Klasse Stack des Listings 9.1 eine generische Klasse Stack<T> (gesprochen: »Stack of T« und nicht nur »Stack«) wird.
// Beispiel: ..\Kapitel 9\GenerischerStack
class Stack<T> {
private readonly int size;
private T[] elements;
private int pointer = 0;
public Stack(int size) {
this.size = size;
elements = new T[size];
}
public void Push(T element) {
if (pointer >= this.size)
throw new StackOverflowException();
elements[pointer] = element;
pointer++;
}
public T Pop() {
pointer--;
if (pointer >= 0)
return elements[pointer];
else {
pointer = 0;
throw new InvalidOperationException("Der Stack ist leer");
}
}
public int Length {
get { return this.pointer; }
}
}
Listing 9.2 Beispiel einer generischen »Stack«-Klasse
Instanziieren Sie die generische Klasse Stack<T>, müssen Sie den Platzhalter T, der als generischer Typparameter bezeichnet wird, durch einen konkreten Datentyp ersetzen. In der folgenden Anweisung handelt es sich beispielsweise um int:
Stack<int> stack = new Stack<int>(10);
Aus der Klasse Stack<T> ist nun ein Objekt vom Typ Stack<int> geworden. Alle Member der Klasse, in denen der Typparameter T verwendet wird, ersetzen ihn nun durch den gewählten konkreten Datentyp und akzeptieren nur noch Integer-Werte (siehe Abbildung 9.1).
Abbildung 9.1 Aufruf der »Push«-Methode der Klasse »Stack<int>«
Den Zugriff auf die generische Klasse Stack<int> zeigt nachfolgend die Methode Main. Der Code ist in einen try-Block gefasst, um ausgelöste Ausnahmen behandeln zu können.
static void Main(string[] args) {
try {
Stack<int> stack = new Stack<int>(10);
stack.Push(123);
stack.Push(4711);
stack.Push(34);
for (int i = stack.Length; i > 0; i--) {
Console.WriteLine(stack.Pop());
}
stack.Pop();
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
Listing 9.3 Auswerten des generischen Stacks aus Listing 9.2
In der for-Schleife werden alle Elemente der Reihe nach vom Stack geholt. Um eine Ausnahme zu provozieren, wird, nachdem der Stack bereits geleert ist, ein weiteres Mal die Pop-Methode aufgerufen.
Die .NET-Klassenbibliothek stellt eine generische Klasse Stack<T> im Namespace System.Collections.Generic zur Verfügung und eine nichtgenerische in System.Collections.
9.2.1 Mehrere generische Typparameter

Die zuvor verwendete generische Klasse Stack<T> definiert einen generischen Datentyp. Je nachdem, wie die Anforderungen an die generische Klasse definiert sind, kann der Bedarf an Typparametern jedoch auch größer sein. Sie könnten durchaus eine Klasse definieren, die zwei oder auch noch mehr generische Typparameter aufweist. Diese werden innerhalb der spitzen Klammern durch ein Komma voneinander getrennt, z. B.: Demo<T, A, B>. Hier handelt es sich sogar um die drei generischen Typparameter T, A und B.
9.2.2 Vorteile der Generics
Der offensichtlich größte Vorteil der Generics ist deren Typsicherheit. Sie programmieren nur eine Klasse und können diese für unterschiedliche Datentypen prägen. Dass die Datentypen sogar bestimmten Bedingungen unterworfen werden können, lernen Sie im nächsten Abschnitt. Verwenden Sie im Programmcode einen generischen Typ, stellt bereits der Compiler fest, wenn Sie einen unzulässigen Datentyp verwenden. Natürlich sorgt auch die Laufzeitumgebung für die Gewährleistung der Typsicherheit.
Neben der Typsicherheit spricht für die Generics das bessere Leistungsverhalten, insbesondere bei der Verwaltung von Wertetypen. Stellen Sie sich nur einfach vor, Sie würden in einer ArrayList zahlreiche Integer-Werte speichern:
ArrayList liste = new ArrayList();
liste.Add(55);
[...]
Obwohl eine ArrayList sehr komfortabel zu handhaben ist, müssen Wertetypen vor der Zuweisung und bei der Auswertung implizit mit dem Boxing- bzw. Unboxing-Verfahren in Referenztypen umgewandelt werden. Das geht, wie in Abschnitt 6.3 beschrieben, zu Lasten der Anwendungsleistung und macht sich insbesondere bei großen Auflistungen deutlich bemerkbar.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.