Generische Typen (Generika) – ÜbersichtGeneric Types (Generics) Overview

Generika werden andauernd in C# verwendet, entweder implizit oder explizit.We use generics all the time in C#, whether implicitly or explicitly. Wenn Sie LINQ in C# verwenden, haben Sie schon einmal bemerkt, dass Sie mit IEnumerable arbeiten?When you use LINQ in C#, did you ever notice that you are working with IEnumerable? Oder haben Sie in einem Onlinebeispiel eines „generischen Repositorys“ zur Kommunikation mit Datenbanken über Entity Framework festgestellt, dass die meisten Methoden IQueryable zurückgeben?Or if you ever saw an online sample of a "generic repository" for talking to databases using Entity Framework, did you see that most methods return IQueryable? Haben Sie sich gefragt, wofür das T in diesen Beispielen steht und warum es dort ist?You may have wondered what the T is in these examples and why is it in there?

Generika wurden zum ersten Mal in .NET Framework 2.0 eingeführt und umfassten Änderungen sowohl an der Sprache C# als auch an der Common Language Runtime (CLR).First introduced to the .NET Framework 2.0, generics involved changes to both the C# language and the Common Language Runtime (CLR). Generika sind im Grunde eine „Codevorlage“, mit der Entwickler typsichere Datenstrukturen definieren können, ohne sich tatsächlich auf einen Datentyp festlegen zu müssen.Generics are essentially a "code template" that allows developers to define type-safe data structures without committing to an actual data type. List<T> ist beispielsweise eine generische Auflistung, die deklariert und mit beliebigen Typen verwendet werden kann: List<int>, List<string>, List<Person> usw.For example, List<T> is a Generic Collection that can be declared and used with any type: List<int>, List<string>, List<Person>, etc.

Worum liegt also der Sinn?So, what’s the point? Warum sind Generika nützlich?Why are generics useful? Um dies zu verstehen, müssen wir uns eine bestimmte Klasse ansehen, und zwar vor und nach dem Hinzufügen von Generika.In order to understand this, we need to take a look at a specific class before and after adding generics. Wir sehen uns ArrayList an.Let’s look at the ArrayList. In C# 1.0 wiesen die ArrayList-Elemente den Typ object auf.In C# 1.0, the ArrayList elements were of type object. Dies bedeutete, dass jedes hinzugefügte Element im Hintergrund in ein object konvertiert wurde. Das Gleiche passierte beim Lesen der Elemente aus der Liste (dieser Prozess wird als Boxing bzw. Unboxing bezeichnet).This meant that any element that was added was silently converted into an object; same thing happens on reading the elements from the list (this process is known as boxing and unboxing respectively). Boxing und Unboxing wirken sich auf die Leistung aus.Boxing and unboxing have an impact of performance. Schlimmer noch: Zum Zeitpunkt der Kompilierung lässt sich nicht mitteilen, welchen Typ die Daten in der Liste tatsächlich aufweisen.More than that, however, there is no way to tell at compile time what is the actual type of the data in the list. Dies beeinträchtigt die Stabilität des Codes.This makes for some fragile code. Generika lösen dieses Problem, indem sie zusätzliche Informationen zum Datentyp bereitstellen, die in jeder Instanz der Liste enthalten sind.Generics solve this problem by providing additional information the type of data each instance of list will contain. Einfach gesagt: Zu List<int> können Sie nur Ganzzahlen hinzufügen, zu List<Person> nur Personen usw.Put simply, you can only add integers to List<int> and only add Persons to List<Person>, etc.

Generika sind auch zur Laufzeit verfügbar und werden konkretisiert.Generics are also available at runtime, or reified. Dies bedeutet, dass die Runtime weiß, welche Art Datenstruktur Sie verwenden, und die Daten effizienter im Arbeitsspeicher speichern kann.This means the runtime knows what type of data structure you are using and can store it in memory more efficiently.

Das folgende Programm zeigt, wie effizient es ist, wenn die Art der Datenstruktur zur Laufzeit bekannt ist:Here is a small program that illustrates the efficiency of knowing the data structure type at runtime:

  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;

  namespace GenericsExample {
    class Program {
      static void Main(string[] args) {
        //generic list
        List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
        //non-generic list
        ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
        // timer for generic list sort
        Stopwatch s = Stopwatch.StartNew();
        ListGeneric.Sort();
        s.Stop();
        Console.WriteLine($"Generic Sort: {ListGeneric}  \n Time taken: {s.Elapsed.TotalMilliseconds}ms");

        //timer for non-generic list sort
        Stopwatch s2 = Stopwatch.StartNew();
        ListNonGeneric.Sort();
        s2.Stop();
        Console.WriteLine($"Non-Generic Sort: {ListNonGeneric}  \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
        Console.ReadLine();
      }
    }
  }

Das Programm erzeugt die folgende Ausgabe:This program yields the following output:

Generic Sort: System.Collections.Generic.List\`1[System.Int32] Time taken: 0.0789ms
Non-Generic Sort: System.Collections.ArrayList Time taken: 2.4324ms

Als erstes bemerken Sie, dass das Sortieren der generischen Liste erheblich schneller ist als bei der nicht generischen Liste.The first thing you notice here is that sorting the generic list is significantly faster than for the non-generic list. Sie stellen vermutlich auch fest, dass der Typ für die generische Liste eindeutig ([System.Int32]), der Typ für die nicht generische Liste dagegen generalisiert ist.You might also notice that the type for the generic list is distinct ([System.Int32]) whereas the type for the non-generic list is generalized. Da der Runtime bekannt ist, dass die generische List<int> den Typ „int“ aufweist, kann sie die Listenelemente in einem zugrunde liegenden ganzzahligen Array im Arbeitsspeicher speichern. Die nicht generische ArrayList dagegen muss jedes Listenelement in ein Objekt umwandeln, das in einem Objektarray im Arbeitsspeicher gespeichert wird.Because the runtime knows the generic List<int> is of type int, it can store the list elements in an underlying integer array in memory while the non-generic ArrayList has to cast each list element as an object as stored in an object array in memory. Wie in diesem Beispiel gezeigt, beanspruchen die Umwandlungen Zeit und verlangsamen den Sortiervorgang für die Liste.As shown through this example, the extra castings take up time and slow down the list sort.

Ein weiterer nützlicher Vorteil: Wenn die Runtime den Typ der Generika kennt, wird das Debuggen vereinfacht.The last useful thing about the runtime knowing the type of your generic is a better debugging experience. Wenn Sie Generika in C# debuggen, wissen Sie, welchen Typ jedes Element in Ihrer Datenstruktur aufweist.When you are debugging a generic in C#, you know what type each element is in your data structure. Ohne Generika wüssten Sie dies nicht.Without generics, you would have no idea what type each element was.

Weitere Informationen und RessourcenFurther reading and resources