Zum Fußzeileninhalt springen
Iron Academy Logo
Lernen Sie C#
Lernen Sie C#

Andere Kategorien

Beherrschung von C# Generics

Tim Corey
38m 51s

Generics in C# sind seit ihrer Einführung zu einem festen Bestandteil der Sprache geworden und bieten zahlreiche Vorteile, auch wenn ihre Funktionsweise nicht von allen Entwicklern vollständig verstanden wird. In seinem Video "How To Create Generics in C#, Including New Features," erklärt Tim Corey, warum Generics wichtig sind, wie man sie erstellt und demonstriert ihre praktischen Anwendungen.

Dieser Artikel bietet einen umfassenden Leitfaden zu C#-Generikern mit wertvollen Erkenntnissen aus dem Video von Tim Corey zu diesem Thema. Es werden die Grundlagen der Generika behandelt, einschließlich Typsicherheit, Leistungsvorteile und praktische Anwendungen. Der Artikel befasst sich auch mit der Erstellung von generischen Methoden, Klassen und Schnittstellen sowie mit der Anwendung von Einschränkungen auf Generika. Außerdem werden reale Anwendungsfälle und die Bedeutung der Verwendung von Generika für das Schreiben von effizientem, typsicherem Code hervorgehoben.

Einführung

C# Generics bieten eine leistungsstarke Möglichkeit, flexiblen, wiederverwendbaren und typsicheren Code zu erstellen, indem sie es Entwicklern ermöglichen, Klassen, Methoden, Schnittstellen und Sammlungen zu definieren, die mit jedem Datentyp funktionieren. Durch die Verwendung einer generischen Klasse oder einer generischen Methode können Entwickler einen generischen Typparameter (z. B. T) definieren, der einen beliebigen Datentyp darstellen kann. Dies eliminiert die Notwendigkeit von Code-Duplikation und verbessert die Wiederverwendbarkeit von Code, während gleichzeitig die Typsicherheit zur Kompilierungszeit sichergestellt wird. Generische Sammlungsklassen wie List und Dictionary<TKey, TValue> ermöglichen eine effiziente Handhabung verschiedener Datentypen, während generische Schnittstellen und generische Delegaten die Erstellung benutzerdefinierter generischer Typen ermöglichen, die mit mehreren Typ-Parametern arbeiten können. Durch den Einsatz von Generika können Entwickler die Code-Effizienz maximieren und die Nachteile von nicht-generischen Klassen minimieren. Diese Flexibilität ermöglicht die Erstellung von mehr wiederverwendbarem Code ohne Einbußen bei der Leistung oder Typsicherheit.

Tim at (0:00) führt in das Thema ein, indem er die weit verbreitete Verwendung von Generics in C# hervorhebt. Er will erklären, warum Generika unerlässlich sind, und zeigen, wie man sie effektiv erstellt und verwendet.

Erstellung des Projekts

Tim beginnt mit der Erstellung einer neuen Konsolenanwendung mit dem Namen "GenericsDemoApp", die sich ausschließlich auf die Demonstration von Generika ohne Ablenkungen durch die Benutzeroberfläche konzentriert. Er richtet das Projekt mit .NET 8 und Visual Studio 2022 ein.

Grundlagen der Generik

Tim beginnt um (2:22) mit einem Beispiel, das die List-Klasse verwendet, um das Konzept der Generika zu veranschaulichen. Generics ermöglichen die Angabe des Typs von Elementen, die eine Sammlung enthalten kann, wodurch Typsicherheit gewährleistet und Laufzeitfehler vermieden werden.

List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };
List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };

Die List stellt sicher, dass nur Ganzzahlen in die Zahlenliste und nur Zeichenfolgen in die Zeichenfolgenliste aufgenommen werden können.

Typensicherheit und Effizienz

Tim hebt die Bedeutung der Typensicherheit hervor, die durch Generics gewährleistet wird. Der Compiler prüft die Typen zur Entwurfszeit, um Fehlanpassungen zu verhindern und eine sichere Codeausführung zu gewährleisten. Generics vermeiden auch die Notwendigkeit von Boxing und Unboxing, was zu effizienterem Code führt.

Ineffizienz von nicht-generischen Sammlungen

Um die Ineffizienz der Verwendung von nicht-generischen Sammlungen zu demonstrieren, erstellt Tim eine Liste und zeigt, wie sie verschiedene Arten von Objekten enthalten kann.

List<object> objects = new List<object> { "Tim", 4, 3.6m };
List<object> objects = new List<object> { "Tim", 4, 3.6m };

Er erklärt, dass die Verwendung nicht-generischer Sammlungen zu Typinkongruenzen und Ineffizienzen aufgrund von Boxing und Unboxing führen kann.

Leistungsvergleich

Tim führt bei (6:15) einen Leistungsvergleich zwischen einer List und einer List durch, um eine Million Elemente hinzuzufügen. Er verwendet eine Stoppuhr, um die verstrichene Zeit für jeden Vorgang zu messen.

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

for (int i = 1; i <= 1_000_000; i++)
{
    objects.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");

stopwatch.Restart();

for (int i = 1; i <= 1_000_000; i++)
{
    numbers.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

for (int i = 1; i <= 1_000_000; i++)
{
    objects.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");

stopwatch.Restart();

for (int i = 1; i <= 1_000_000; i++)
{
    numbers.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");

Die Ergebnisse zeigen, dass das Hinzufügen von ganzen Zahlen zu einer List deutlich schneller ist als das Hinzufügen zu einer List, da es kein Boxing und Unboxing gibt.

Leistungsvergleich

Erstellen einer Type Checker Methode

Tim demonstriert bei (10:14), wie man eine generische Methode namens TypeChecker erstellt. Diese Methode prüft den Typ eines gegebenen Wertes und gibt ihn aus, um die Flexibilität und Leistungsfähigkeit von Generika zu veranschaulichen.

public static void TypeChecker<t>(T value)
{
    Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}
public static void TypeChecker<t>(T value)
{
    Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}

Die TypeChecker-Methode verwendet den typeof-Operator, um den Typ des generischen Parameters T zu bestimmen und gibt sowohl den Typ als auch den Wert aus.

Verwendung der Type Checker Methode

Tim gibt Beispiele für den Aufruf der TypeChecker-Methode mit verschiedenen Arten von Argumenten.

TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1
TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1

Durch die Übergabe verschiedener Typen an die TypeChecker-Methode zeigt Tim, wie Generics nahtlos mit verschiedenen Datentypen umgehen können.

Erstellen einer generischen Klasse: Bessere Liste

Tim geht bei (16:25) dazu über, eine generische Klasse namens BetterList zu erstellen. Diese Klasse kapselt eine Liste eines bestimmten Typs und bietet zusätzliche Funktionen.

public class BetterList<t>
{
    private List<t> data = new List<t>();

    public void AddToList(T value)
    {
        data.Add(value);
        Console.WriteLine($"{value} has been added to the list");
    }
}
public class BetterList<t>
{
    private List<t> data = new List<t>();

    public void AddToList(T value)
    {
        data.Add(value);
        Console.WriteLine($"{value} has been added to the list");
    }
}

Die BetterList-Klasse enthält eine private List und eine Methode AddToList, die einen Wert zur Liste hinzufügt und eine Nachricht druckt, welche die Hinzufügung anzeigt.

Verwendung der Klasse Better List

Tim gibt Beispiele für die Verwendung der BetterList-Klasse mit verschiedenen Typen.

BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);

BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));
BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);

BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));

In diesen Beispielen verwaltet BetterList eine Liste von Ganzzahlen, während BetterList eine Liste von PersonRecord-Objekten verwaltet. Jede Ergänzung der Liste löst eine Konsolenmeldung aus, die den zusätzlichen Wert angibt.

Erstellen einer generischen Schnittstelle

Tim stellt bei (21:48) die Idee einer generischen Schnittstelle namens IImportance vor. Diese Schnittstelle definiert eine Methode, um zu bestimmen, welcher von zwei Werten wichtiger ist.

public interface IImportance<t>
{
    T MostImportant(T a, T b);
}
public interface IImportance<t>
{
    T MostImportant(T a, T b);
}

Implementierung der generischen Schnittstelle

Tim zeigt, wie man diese Schnittstelle für verschiedene Typen implementiert. Er beginnt mit einer Integer-Implementierung.

public class EvaluateImportance : IImportance<int>
{
    public int MostImportant(int a, int b)
    {
        return a > b ? a : b;
    }
}
public class EvaluateImportance : IImportance<int>
{
    public int MostImportant(int a, int b)
    {
        return a > b ? a : b;
    }
}

Als Nächstes implementiert er die Schnittstelle für Zeichenketten, wobei er die Länge der Zeichenketten zur Bestimmung der Wichtigkeit verwendet.

public class EvaluateStringImportance : IImportance<string>
{
    public string MostImportant(string a, string b)
    {
        return a.Length > b.Length ? a : b;
    }
}
public class EvaluateStringImportance : IImportance<string>
{
    public string MostImportant(string a, string b)
    {
        return a.Length > b.Length ? a : b;
    }
}

Diese Implementierungen zeigen, wie dieselbe Schnittstelle auf verschiedene Typen mit spezifischer Logik für jeden Typ angewendet werden kann.

Einschränkungen auf Generika anwenden

Tim erklärt bei (25:21), wie man Einschränkungen auf Generika anwendet, um sicherzustellen, dass sie bestimmte Bedingungen erfüllen. Ein generischer Typ kann zum Beispiel darauf beschränkt werden, einen leeren Konstruktor zu haben oder eine bestimmte Schnittstelle zu implementieren.

public class SampleClass<t> where T : new()
{
    // Class implementation
}

public class SampleClassWithInterface<t> where T : IImportance<t>
{
    // Class implementation
}
public class SampleClass<t> where T : new()
{
    // Class implementation
}

public class SampleClassWithInterface<t> where T : IImportance<t>
{
    // Class implementation
}

Diese Einschränkungen tragen dazu bei, dass der generische Typ die erforderlichen Kriterien erfüllt, um Laufzeitfehler zu vermeiden und die Typsicherheit zu erhöhen.

Microsoft's Implementierung von INumber

Tim erörtert, wie Microsoft die INumber-Schnittstelle verwendet, um numerische Operationen einzuschränken. Dies ermöglicht arithmetische Operationen wie Addition und Subtraktion bei generischen Typen.

public class MathOperations<t> where T : INumber<t>
{
    public T Add(T x, T y)
    {
        return x + y;
    }
}
public class MathOperations<t> where T : INumber<t>
{
    public T Add(T x, T y)
    {
        return x + y;
    }
}

Durch die Einschränkung des generischen Typs T auf INumber wird sichergestellt, dass der Typ numerische Operationen unterstützt.

Verwendung von Generics mit verschiedenen numerischen Typen

Tim (33:55) geht auf die Klasse MathOperations ein, um zu zeigen, wie Generika mit verschiedenen numerischen Typen wie Doubles und Dezimalzahlen verwendet werden können.

Tim zeigt, wie man Instanzen von MathOperations für Integers und Doubles erstellt:

MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5

MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8
MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5

MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8

Dies demonstriert die Flexibilität von Generika, die es ermöglichen, verschiedene numerische Typen nahtlos innerhalb derselben Klasse zu behandeln.

Behandlung verschiedener numerischer Typen

Tim unterstreicht die Bedeutung der Typensicherheit, indem er zeigt, dass man verschiedene numerische Typen nicht mischen kann. Wenn man zum Beispiel versucht, ein Double zu einem Integer zu addieren, führt dies zu einem Kompilierungsfehler.

// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));
// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));

Vermeiden Sie den Aufwand für die Typkonvertierung

Tim erklärt die Vorteile der Verwendung von Generika, um den mit der Typkonvertierung verbundenen Overhead zu vermeiden. Beispielsweise kann die Konvertierung von Ganzzahlen in Doppelzahlen für mathematische Operationen und dann zurück in Ganzzahlen kostspielig sein. Die Verwendung von Generika ermöglicht direkte Operationen mit den nativen Typen, wobei Leistung und Präzision erhalten bleiben.

Generics in der Praxis

Tim rät zur Vorsicht bei der Verwendung von Generika und empfiehlt den Entwicklern, sie angemessen einzusetzen und eine übermäßige Verwendung zu vermeiden. Er hebt die Vorteile von Generika hervor, wie z. B. Typsicherheit, reduziertes Boxing und Unboxing, Kompilierzeitprüfung und verbesserte Lesbarkeit des Codes.

Er weist auch darauf hin, dass Generika weit verbreitet sind in Sammlungen wie List und Dictionary<TKey, TValue> sowie in Protokollierungsframeworks, die verschiedene Typen verarbeiten können, ohne im Voraus die Einzelheiten kennen zu müssen.

Abschluss

Tim Coreys detaillierte Untersuchung der fortgeschrittenen Generics in C# bietet wertvolle Einblicke in ihre praktischen Anwendungen und Vorteile. Wenn Sie Ihr Verständnis von Generics vertiefen und Beispiele aus der Praxis in Aktion sehen möchten, sollten Sie sich unbedingt Tim Coreys ausführliches Video zu C# Generics ansehen. Seine klaren Erklärungen und praktischen Demonstrationen werden Ihnen helfen, die Konzepte vollständig zu erfassen und sie in Ihren eigenen Projekten effektiv anzuwenden.

Hero Worlddot related to Beherrschung von C# Generics
Hero Affiliate related to Beherrschung von C# Generics

Verdienen Sie mehr, indem Sie teilen, was Sie lieben

Erstellen Sie Inhalte für Entwickler, die mit .NET, C#, Java, Python oder Node.js arbeiten? Verwandeln Sie Ihr Fachwissen in ein zusätzliches Einkommen!

Iron Support Team

Wir sind 24 Stunden am Tag, 5 Tage die Woche online.
Chat
E-Mail
Rufen Sie mich an