Vorweg: Es hat sich eingebürgert, dass generiche Typ-Parameter immer mit einem T beginnen. Wie das I bei einem Interface ^^
Wozu Generics da sind:
Du hast doch sicher schon mal mit einer Liste gearbeitet, oder? Die ist auch generich.
Du gibst der den Typ an, mit der sie arbeiten soll und ab diesem Zeitpunkt hast Du überall, wo dieser generische Typ verwendet wird, genau den Typ, den Du angegeben hast. Eine List<string> verlangt in der Add-Methode einen string und auch der Indexer arbeitet mit einem String. Gibst Du Int als Typ an, hast Du überall Int.
Fällt der generische Aspekt weg (wie bei der ArrayList), dann kannst Du zwar auch sagen, in der Liste sollen Strings sein, aber Du kannst genauso gut auch ein Int hinzufügen. Wenn Du dann die Strings in der Liste auswerten willst, bekommst Du einen Fehler.
Die generische Liste hat damit den Vorteil, dass sie mit jedem Typen funktioniert, typisiert bleibt und der Compiler das sogar nicht überprüft.
Zusätzlich hast Du die Möglichkeit, dem generischen Typ-Parameter Einschränkungen zu verpassen. Z.B. kannst Du sagen, dass der Typ ein bestimmtes Interface implementieren soll, oder es soll eine Klasse sein, oder einen parameterlosen Konstruktor haben.
Wenn Du dann diesen generischen Typ-Parameter verwendest, kannst Du auf diese Einschrönkungen aufbauen. So hat der Typ dann alle Member des Interfaces, ein Objekt davon darf null sein (Was z.B. den as-Operator möglich macht) und Du kannst eine Instanz erstellen - und das ohne zu wissen, was Du da eigentlich hast.
Gleichzeitig wird bei der Nutzung der generischen Klasse darauf geachtet, dass der Typ, den Du übergibst, auch den Anforderungen entspricht.
Bei Methoden hast Du die gleichen Vorteile.
Ein sehr gutes Beispiel ist z.B. Linq, denn fast jede Erweiterungsmethode ist generisch.
So kannst Du bei der Where-Methode eine Function übergeben, die die Bedingung für das Where dar stellt. Die Methode weiß eigentlich nicht, welcher Typ in deiner Auflistung ist, anhand der Auflistung, die auch generisch ist, kann das Compiler das aber automatisch heraus finden und sorgt dann im selben Zuge dafür, dass deine Funktion auch den richtigen Typ als Parameter bekommt.
Und der Rückgabetyp der Methode ist wieder eine Auflistung mit genau dem Typen, den Du haben willst.
Wären die Methoden nicht generisch, dann müssten sie untypisiert sein. Dann sähe der Aufruf der Where-Methode z.B. so aus:
C#-Quelltext
1: 2: 3: 4: 5:
| var list = new ArrayList() { 1, 2, 3, 4, 5 }; var filtered = list.Where(item => (int)item > 3); var summe = 0; foreach (var item in filtered) summe += (int)item; |
Gibst Du der ursprünglichen Liste einen String, fliegt dir der ganze Code um die Ohren.
generisch:
C#-Quelltext
1: 2: 3: 4: 5:
| var list = new List<int>() { 1, 2, 3, 4, 5 }; var filtered = list.Where(item => item > 3); var summe = 0; foreach (var item in filtered) summe += item; |
Gibst Du der ursprünglichen Liste einen String, lässt dich der Compiler nicht einmal compilieren und zeigt dir direkt wo genau was für ein Fehler ist.
Und ja, ich weiß, dass es auch eine Sum-Methode gibt
Insgesamt kannst Du mit Generics Klassen und Methoden schreiben, die unabhängig vom Typ sind und mit gleicher Funktionsweise für unterschiedliche Daten immer wieder verwendet werden können.4
Und oben drauf passt der Compiler auch noch auf, dass Du keinen Mist machst.