Generic

Aus Das Sopra Wiki
Wechseln zu: Navigation, Suche


Allgemein

Generics ermögliches es einer Klasse, Typen zu verwenden, die zur Compilezeit dieser Klasse noch nicht bekannt waren. Dafür werden der Klasse, typischerweise bei der Instanziierung, ein oder mehrere Typparameter mitgegeben.

Generische Klassen

Generische Klassen werden normalerweise für Listen oder Bäume verwendet. Ein Beispiel ist die Klasse List<T> im Namensraum (Namespace) System.Collections.Generic. Dieser Klasse muss man beim Instanziieren einen Parameter mitgeben, der angibt, welche Typen die Instanzen der Elemente, die später in der Liste gespeichert werden sollen, haben.

Verwendung

List<Math> liste = new List<Math>();
liste.Add(new Math(5));

Der Liste list können nun nur Objekte vom Typ Math und deren Subtypen übergeben werden (Math ist hier der Wert, der dem Typparameter T zugewiesen wird). Folglich gibt der Compiler eine Fehlermeldung aus, wenn ein falscher Typ in die Liste eingefügt wird:

list.Add(new Object());

Durch diese Funktionalität können viele Typisierungs-Probleme schon während des Kompilierens und nicht erst zur Laufzeit erkannt werden.

Definition

Die Definition einer generischen Klasse mit einem Typparameter T sieht zum Beispiel so aus:

public class List<T>
{
    ...
}

Eine Klasse kann auch mehrere Typparameter erwarten. Ein Beispiel für solch eine Klasse ist Dictionary

public class Dictionary<K, V>
{
    ...
}

In diesem Beispiel sind K und V die Typparameter, informell bezeichnet K den Key-Typ, V den Value-Typ des Dictionaries.

Mehr Informationen zum Verwenden und Definieren von generischen Klassen findet sich im C# Programming Guide unter Generic Classes.

Generische Methoden

Man kann auch Methoden schreiben, die einen bestimmmten Typ erwarten oder zurückgeben. Eine solche Methode ist z.B. ElementAt(int i) .
public T ElementAt(int i)
{
    return m_Elements[i];
}

Hier ist der Typparameter durch die Klasse festgelegt. Eine Methode kann aber auch einen eigenen Typparameter erwarten, denkbar wäre eine generische Factory-Methode CreateInstance<T>(). Diese könnte z.B. folgende Definition haben:

public T CreateInstance<T>()
{
    return new T();
}
...
Raumgleiter rg = CreateInstance<Raumgleiter>();

Teilweise kann dies auch der Compiler tun sodaß der Programmierer diese nicht manuell angeben muß. Mit einer solchen Methode könnte man zum Beispiel den Hash zweier Objekte vergleichen.

public T GetObjectWithSmallerHash<T>(T objectEins, T objectZwei)
{
    if(objectEins.GetHashCode() < objectZwei.GetHashCode())
    {
        return objectEins;
    }
    return objectZwei;
}

Benutzt werden kann diese Funktion ohne Typparameter, dieser wird aus den übergebenen Objekten generiert.

Raumgleiter rEins = new Raumgleiter();
Raumgleiter rZwei = new Raumgleiter();
Raumgleiter rMitNiedrigeremHash = GetObjectWithSmallerHash(rEins, rZwei);

Man beachte hier, daß kein Cast notwendig ist sondern direkt ein richtig typisiertes Objekt zurückgegeben wird. Dies wird auch bei Lambda-Ausdrücken verwendet.

Typen einschränken

Um auf den Objekten andere Methoden auszuführen als die, die schon durch Object definiert sind, muß man die Typparameter einschränken. Für eine sortierte Liste wäre eine sinnvolle Einschränkung alle Klassen, die IComparable implementieren.

public class SortedList<T> where T : IComparable
{
    ...
}

Hier kann man auch die Methode CompareTo benutzen, die nicht in Object definiert ist. Dies geht natürlich auch mit mehreren Typparametern.

Konvention

Für Typparameter hat sich die Konvention eines Großbuchstabens eingebürgert. Man kann durchaus auch andere, nicht belegte Namen benutzen allerdings wird davon abgeraten. Dies hat den Hintergrund, daß Klassennamen normalerweise nicht nur aus einem einzigen Buchstaben bestehen. Als Empfehlung gilt T für Klassen mit einem Typparameter und K (wie Key') und V (wie Value) für Dictionaries.