Generics in .NET

Mit Generics können Sie eine Methode, Klasse, Struktur oder Schnittstelle genau an den Datentyp anpassen, der von ihnen verarbeitet wird. Anstatt beispielsweise die Hashtable -Klasse zu verwenden, bei der Schlüssel und Werte einen beliebigen Typ haben dürfen, können Sie die generische Klasse Dictionary<TKey,TValue> verwenden und die für den Schlüssel und Wert zulässigen Typen angeben. Zu den Vorteilen von Generics zählen bessere Wiederverwendbarkeit des Codes und Typsicherheit.

Definieren und Verwenden von Generics

Generics sind Klassen, Strukturen, Schnittstellen und Methoden, die über Platzhalter (Typparameter) für einen oder mehrere der Typen verfügen, die sie speichern oder verwenden. Eine generische Auflistungsklasse kann beispielsweise einen Typparameter als Platzhalter für den Typ von Objekten verwenden, die in ihr gespeichert werden; die Typparameter werden als die Typen ihrer Felder und die Parametertypen ihrer Methoden angezeigt. Eine generische Methode kann ihren Typparameter als den Typen ihres Rückgabewerts oder als den Typen einer ihrer formalen Parameter verwenden. Der folgende Code zeigt eine einfache generische Klassendefinition.

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

Beim Erstellen einer Instanz einer generischen Klasse geben Sie die tatsächlich zu ersetzenden Typen für die Typparameter an. Dadurch wird eine neue generische Klasse eingerichtet, die als konstruierte generische Klasse bezeichnet wird, wobei die ausgewählten Typen überall dort, wo die Typparameter angezeigt werden, ersetzt werden. Das Ergebnis ist eine typsichere Klasse, die auf die ausgewählten Typen zugeschnitten ist, wie im folgenden Code veranschaulicht wird.

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Generics-Terminologie

Die folgenden Begriffe werden bei der Erörterung von Generics in .NET verwendet:

  • Eine generische Typdefinition ist eine Klassen-, Struktur- oder Schnittstellendeklaration, die als Vorlage fungiert und Platzhalter für die Typen besitzt, die sie enthalten oder verwenden kann. Die System.Collections.Generic.Dictionary<TKey,TValue> -Klasse kann beispielsweise zwei Typen enthalten: Schlüssel und Werte. Da eine generische Typdefinition lediglich eine Vorlage ist, können Sie keine Instanzen einer Klasse, Struktur oder Schnittstelle erstellen, die eine generische Typdefinition ist.

  • Generische Typparameter, oder Typparameter, sind die Platzhalter in einer generischen Typ- oder Methodendefinition. Der generische Typ System.Collections.Generic.Dictionary<TKey,TValue> verfügt über zwei Typparameter, TKey und TValue, die die Typen seiner Schlüssel und Werte darstellen.

  • Ein konstruierter generischer Typ, oder konstruierter Typ, ist das Ergebnis der Angabe von Typen für die generischen Typparameter einer generischen Typdefinition.

  • Ein generisches Typargument ist jeder Typ, der durch einen generischen Typparameter ersetzt wird.

  • Der allgemeine Begriff generischer Typ schließt sowohl konstruierte Typen als auch generische Typdefinitionen ein.

  • Kovarianz und Kontravarianz der generischen Typparameter ermöglichen es Ihnen, konstruierte generische Typen zu verwenden, deren Typargumente stärker abgeleitet (Kovarianz) oder weniger stark abgeleitet (Kontravarianz) sind als ein konstruierter Zieltyp. Kovarianz und Kontravarianz werden zusammen als Varianzbezeichnet. Weitere Informationen finden Sie unter Kovarianz und Kontravarianz.

  • Einschränkungen sind Begrenzungen für generische Typparameter. Sie können beispielsweise einen Typparameter auf Typen beschränken, die die generische Schnittstelle System.Collections.Generic.IComparer<T> implementieren, um sicherzustellen, dass Instanzen des Typs sortiert werden können. Sie können Typparameter auch auf Typen mit einer bestimmten Basisklasse beschränken, die über einen parameterlosen Konstruktor verfügen oder die Verweis- oder Werttypen sind. Benutzer des generischen Typs können keine Typargumente ersetzen, die die Einschränkungen nicht erfüllen.

  • Eine generische Methodendefinition ist eine Methode mit zwei Parameterlisten: eine Liste der generischen Typparameter und eine Liste der formalen Parameter. Typparameter können als Rückgabetyp oder als Typen der formalen Parameter auftreten, wie im folgenden Code dargestellt.

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

Generische Methoden können in generischen oder nicht generischen Typen stehen. Beachten Sie unbedingt, dass eine Methode nicht nur deshalb generisch ist, weil sie zu einem generischen Typ gehört oder weil sie formale Parameter besitzt, deren Typen die generischen Parameter des einschließenden Typs sind. Eine Methode ist nur generisch, wenn sie über eine eigene Liste an Typparametern verfügt. Im folgenden Code ist nur die Methode G generisch.

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

Vor- und Nachteile von Generics

Es gibt viele Vorteile für die Verwendung von generischen Auflistungen und Delegaten:

  • Typsicherheit. Generics übertragen die Last der Typsicherheit an den Compiler. Es ist nicht erforderlich, dass Sie Code schreiben, um auf den richtigen Datentyp hin zu testen, da dieser zum Zeitpunkt der Kompilierung erzwungen wird. Die Notwendigkeit einer Typumwandlung und die Möglichkeit von Laufzeitfehlern werden verringert.

  • Es ist weniger Code erforderlich, und Code kann leichter wiederverwendet werden. Die Vererbung von einem Basistyp oder das Überschreiben von Membern ist nicht erforderlich. LinkedList<T> ist beispielsweise sofort einsatzbereit. So können Sie mit der folgenden Variablendeklaration eine verknüpfte Liste von Zeichenfolgen erstellen:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Bessere Leistung. Generische Auflistungstypen bieten im Allgemeinen eine bessere Leistung zum Speichern und Bearbeiten von Werttypen, da das Boxing von Werttypen nicht erforderlich ist.

  • Generische Delegaten ermöglichen typsichere Rückrufe, ohne mehrere Delegatklassen erstellen zu müssen. Der generische Delegat Predicate<T> ermöglicht es Ihnen beispielsweise, Ihre eigenen Suchkriterien für einen bestimmten Typ zu implementieren und Ihre Methode für Methoden vom Typ Array zu verwenden, z. B. Find, FindLastund FindAll.

  • Generics optimieren dynamisch generierten Code. Wenn Sie Generics mit dynamisch generiertem Code verwenden, müssen Sie nicht den Typ generieren. Dies erhöht die Anzahl der Szenarios, in denen Sie einfache dynamische Methoden verwenden können, anstatt ganze Assemblys zu generieren. Weitere Informationen finden Sie unter Vorgehensweise: Definieren und Ausführen von dynamischen Methoden und DynamicMethod.

Es folgen einige Einschränkungen von Generics:

  • Generische Typen können von den meisten Basisklassen abgeleitet werden, z. B. MarshalByRefObject (und Einschränkungen können verwendet werden, um festzulegen, dass generische Typparameter von Basisklassen wie MarshalByRefObjectabgeleitet werden). .NET unterstützt allerdings keine kontextgebundenen generischen Typen. Ein generischer Typ kann von ContextBoundObjectabgeleitet werden, aber wenn Sie versuchen, eine Instanz dieses Typs zu erstellen, wird eine TypeLoadExceptionausgelöst.

  • Enumerationen können keine generischen Typparameter haben. Eine Enumeration kann nur durch Zufall generisch sein (z. B. weil sie in einem generischen Typ verschachtelt ist, der mit Visual Basic, C# oder C++ definiert wurde). Weitere Informationen finden Sie im Abschnitt "Enumerationen" unter Allgemeines Typsystem.

  • Einfache dynamische Methoden können nicht generisch sein.

  • In Visual Basic, C# und C++ kann ein geschachtelter Typ, der in einem generischen Typ eingeschlossen ist, nicht instanziiert werden, es sei denn, die Typen wurden den Typparametern aller einschließenden Typen zugewiesen. Anders ausgedrückt: Ein geschachtelter Typ, der mit diesen Sprachen definiert wurde, enthält bei näherer Betrachtung die Typparameter aller einschließenden Typen. Dadurch können die Typparameter von einschließenden Typen in den Memberdefinitionen eines geschachtelten Typs verwendet werden. Weitere Informationen finden Sie unter "Geschachtelte Typen" in MakeGenericType.

    Hinweis

    Ein geschachtelter Typ, der durch Ausgabe von Code in einer dynamischen Assembly oder durch Verwendung von Ilasm.exe (IL -Assembler) definiert wird, ist nicht erforderlich, um die Typparameter seiner einschließenden Typen zu beinhalten; wenn sie jedoch nicht enthalten sind, befinden sich die Typparameter nicht im Gültigkeitsbereich der geschachtelten Klasse.

    Weitere Informationen finden Sie unter "Geschachtelte Typen" in MakeGenericType.

Klassenbibliothek und Sprachunterstützung

.NET bietet eine Reihe generischer Auflistungsklassen in den folgenden Namespaces:

Generische Schnittstellen für die Implementierung von Sortier- und Übereinstimmungsvergleichen finden Sie im System -Namespace zusammen mit generischen Delegattypen für Ereignishandler, Konvertierungen und Suchprädikate.

Dem System.Reflection -Namespace wurde Unterstützung für Generics hinzugefügt, um generische Typen und generische Methoden zu überprüfen. Sie wurde auch System.Reflection.Emit zum Ausgeben von dynamischen Assemblys mit generischen Typen und Methoden und System.CodeDom zum Generieren von Quelldiagrammen mit Generics hinzugefügt.

Die Common Language Runtime bietet neue Opcodes und Präfixe für die Unterstützung generischer Typen in Microsoft Intermediate Language (MSIL), darunter Stelem, Ldelem, Unbox_Any, Constrainedund Readonly.

Visual C++, C# und Visual Basic bieten alle vollständige Unterstützung für das Definieren und Verwenden von Generics. Weitere Informationen zur Sprachunterstützung finden Sie unter Generische Typen in Visual Basic, Einführung in Generics und Übersicht über Generics in C++.

Geschachtelte Typen und Generics

Ein Typ, der in einem generischen Typ geschachtelt ist, kann von den Typparametern des einschließenden generischen Typs abhängen. Die Common Language Runtime betrachtet geschachtelte Typen als generisch, auch wenn sie keinen eigenen generischen Typparameter haben. Beim Erstellen einer Instanz eines geschachtelten Typs müssen Sie Typargumente für alle einschließenden generischen Typen festlegen.

Titel Beschreibung
Generische Sammlungen in .NET Beschreibt generische Auflistungsklassen und andere generische Auflistungstypen in .NET.
Generische Delegaten zum Bearbeiten von Arrays und Listen Beschreibt generische Delegate für Konvertierungen, Suchprädikate und Aktionen, die für Elemente eines Arrays oder einer Auflistung ausgeführt werden.
Generische Schnittstellen Beschreibt generische Schnittstellen, die allgemeine Funktionalität für Familien generischer Typen bereitstellen.
Kovarianz und Kontravarianz Beschreibt Kovarianz und Kontravarianz in generischen Typparametern.
Häufig verwendete Auflistungstypen Enthält zusammenfassende Informationen über die Merkmale und Verwendungsszenarios der Auflistungstypen in .NET, einschließlich generischer Typen.
Verwenden von generischen Auflistungen Beschreibt die allgemeinen Regeln, um festzulegen, wann generische Auflistungstypen verwendet werden können.
How to: Definieren eines generischen Typs mit Reflektionsausgabe Erläutert das Generieren dynamischer Assemblys, die generische Typen und Methoden enthalten.
Generische Typen in Visual Basic (Visual Basic) Beschreibt das Genericsfeature für Visual Basic-Benutzer, darunter Gewusst-wie-Themen zum Verwenden und Definieren generischer Typen.
Einführung in Generics Bietet eine Übersicht über das Definieren und Verwenden generischer Typen für C#-Benutzer.
Übersicht über Generics in Visual C++ Beschreibt das Genericsfeature für C++-Benutzer, darunter die Unterschiede zwischen Generics und Vorlagen.

Referenz

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes