EMZETT.
Login

Kurz: Generics erlauben Code, der mit beliebigen Typen arbeitet, bei voller Typsicherheit (List<int>, Dictionary<string, Person>).

Teil des Kurses C#

Kapitel 11 von 16 im Kurs C# (Abschnitt „Fortgeschritten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Generics

Generics erlauben Code, der mit beliebigen Typen arbeitet, bei voller Typsicherheit (List<int>, Dictionary<string, Person>).

Console.WriteLine(Groesser(3, 9) + " " + Groesser("kiwi", "apfel") + " " + Groesser(2.5, 1.5));
var s = new Stapel<int>();
s.Push(1); s.Push(2); s.Push(3);
Console.WriteLine($"{s.Pop()} {s.Anzahl}");
var t = new Stapel<string>();
t.Push("a");
Console.WriteLine(t.Pop());
 
T Groesser<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b;
 
class Stapel<T>
{
    private readonly List<T> _daten = [];
    public int Anzahl => _daten.Count;
    public void Push(T x) => _daten.Add(x);
    public T Pop()
    {
        if (_daten.Count == 0) throw new InvalidOperationException("leer");
        T x = _daten[^1];
        _daten.RemoveAt(_daten.Count - 1);
        return x;
    }
}

Ausgabe:

9 kiwi 2.5
3 2
a

Mit where schränkst du Typparameter ein: where T : class, struct, new(), IComparable<T>, Basisklasse.

LINQ: Abfragen auf Sammlungen

LINQ (Language Integrated Query) bietet Methoden zum Filtern, Umwandeln, Sortieren und Gruppieren von Daten. Das Ergebnis ist meist ein IEnumerable<T>:

var leute = new List<Person>
{
    new("Mia", 17, "Köln"), new("Tom", 19, "Berlin"), new("Zoe", 21, "Köln"), new("Ben", 15, "Bonn"),
};
 
var volljaehrig = leute.Where(p => p.Alter >= 18).Select(p => p.Name).OrderBy(n => n).ToList();
Console.WriteLine(string.Join(", ", volljaehrig));
 
Console.WriteLine($"{leute.Average(p => p.Alter)} {leute.Max(p => p.Alter)} {leute.Count(p => p.Stadt == "Köln")}");
Console.WriteLine($"{leute.Any(p => p.Alter > 20)} {leute.All(p => p.Alter > 16)} {leute.FirstOrDefault(p => p.Alter > 100)?.Name ?? "keiner"}");
 
foreach (var gruppe in leute.GroupBy(p => p.Stadt).OrderBy(g => g.Key))
    Console.WriteLine($"{gruppe.Key}: {string.Join(", ", gruppe.Select(p => p.Name))} (Ø {gruppe.Average(p => p.Alter):F1})");
 
var nachAlter = leute.OrderByDescending(p => p.Alter).ThenBy(p => p.Name).Take(2).Select(p => p.Name);
Console.WriteLine(string.Join(" ", nachAlter));
Console.WriteLine(string.Join(",", Enumerable.Range(1, 5).Select(x => x * x)));
Console.WriteLine(leute.ToDictionary(p => p.Name, p => p.Alter)["Zoe"]);
 
record Person(string Name, int Alter, string Stadt);

Ausgabe:

Tom, Zoe
18 21 2
True False keiner
Berlin: Tom (Ø 19.0)
Bonn: Ben (Ø 15.0)
Köln: Mia, Zoe (Ø 19.0)
Zoe Tom
1,4,9,16,25
21
MethodeWirkung
Wherefiltern
Selectumwandeln
SelectManyumwandeln und flachdrücken
OrderBy, ThenBy, OrderByDescendingsortieren
GroupBygruppieren
Take, Skiperste n / überspringen
DistinctDuplikate entfernen
First, FirstOrDefault, Singleein Element
Any, All, Containsprüfen
Count, Sum, Average, Min, MaxAuswertungen
Aggregatefalten
ToList, ToArray, ToDictionary, ToHashSetin Sammlung umwandeln
Zip, Concat, Union, Intersect, ExceptMengen und Verbinden

Verzögerte Ausführung

LINQ-Abfragen laufen erst, wenn du das Ergebnis brauchst (foreach, ToList, Count):

var zahlen = new List<int> { 1, 2, 3 };
var abfrage = zahlen.Where(x => { Console.Write($"[prüfe {x}]"); return x > 1; });
Console.WriteLine("Abfrage definiert");
zahlen.Add(4);                                   // wird noch berücksichtigt!
Console.WriteLine(string.Join(",", abfrage));
var fest = zahlen.Where(x => x > 1).ToList();    // sofort ausgeführt, Ergebnis fixiert
zahlen.Add(5);
Console.WriteLine(fest.Count);

Ausgabe:

Abfrage definiert
[prüfe 1][prüfe 2][prüfe 3][prüfe 4]2,3,4
3

Abfragesyntax

Alternativ gibt es eine SQL-ähnliche Schreibweise:

var zahlen = Enumerable.Range(1, 10);
var ergebnis = from z in zahlen
               where z % 2 == 0
               orderby z descending
               select z * z;
Console.WriteLine(string.Join(" ", ergebnis));

Ausgabe:

100 64 36 16 4

Eigene Sequenzen mit yield

foreach (var f in Fibonacci().Take(10)) Console.Write(f + " ");
Console.WriteLine();
Console.WriteLine(string.Join(",", Teilbare(30, 7)));
 
IEnumerable<long> Fibonacci()
{
    long a = 0, b = 1;
    while (true) { yield return a; (a, b) = (b, a + b); }              // unendlich, lazy
}
IEnumerable<int> Teilbare(int bis, int teiler)
{
    for (int i = 1; i <= bis; i++) if (i % teiler == 0) yield return i;
}

Ausgabe:

0 1 1 2 3 5 8 13 21 34
7,14,21,28

Merke

  • Generics: class Stapel<T>, T Methode<T>(T x); where T : ... schränkt ein
  • LINQ: Where, Select, OrderBy, GroupBy, Sum, Any, ToList und mehr
  • Abfragen sind lazy: Sie laufen erst bei foreach/ToList/Count
  • yield return erzeugt eigene Sequenzen
  • LINQ ersetzt viele handgeschriebene Schleifen, bleibt aber lesbar

Übungsaufgabe

Gruppiere eine Liste von Bestellungen nach Kunde, berechne Summe und Anzahl je Kunde und gib die Top 3 aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Null-Sicherheit und Ausnahmen

Weiter: Delegates, Lambdas und Events

Alle Kapitel: C# im Überblick