Generics (Klassen & Methoden)
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 42 von 70.
Generics ermöglichen es Ihnen, Klassen und Methoden zu schreiben, die mit jedem Datentyp arbeiten, während die Typsicherheit gewahrt bleibt. Anstatt separate Klassen für IntList, StringList und DoubleList zu schreiben, schreiben Sie eine generische Klasse, die sie alle verarbeitet.
Eine generische Klasse verwendet einen Typparameter, der normalerweise T benannt ist und in spitzen Klammern steht:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}
// Verwendung mit verschiedenen Typen
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Wenn Sie Box<int> erstellen, ersetzt der Compiler jedes T durch int. Dies bietet eine Typprüfung zur Kompilierzeit – Sie können nicht versehentlich einen String in einer int-Box speichern.
Generische Methoden funktionieren ähnlich und ermöglichen es einer einzelnen Methode, mit verschiedenen Typen zu arbeiten:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // x=2, y=1Der Compiler leitet den Typ aus den Argumenten ab, sodass Sie Swap<int> nicht explizit schreiben müssen. Generika sind die Grundlage von Sammlungen wie List<T> und Dictionary<TKey, TValue>, was wiederverwendbaren, typsicheren Code ermöglicht.
Aufgabe
EinfachLassen Sie uns ein generisches Storage<T>-System bauen, das Elemente eines beliebigen Typs aufnehmen und verwalten kann. Sie werden eine wiederverwendbare Container-Klasse erstellen, die die Leistungsfähigkeit von Generics demonstriert - einmal schreiben, mit Integern, Strings oder jedem anderen Typ verwenden!
Sie werden Ihren Code in zwei Dateien organisieren:
Storage.cs: Erstellen Sie eine generischeStorage<T>-Klasse im NamespaceDataStorage. Ihr Speicher-Container sollte:- Ein privates Array vom Typ
Tbesitzen, um Elemente zu halten, sowie eineCount-Eigenschaft, die verfolgt, wie viele Elemente gespeichert sind - Eine Kapazität in seinem Konstruktor akzeptieren, um das interne Array zu initialisieren
- Eine
Add(T item)-Methode haben, die ein Element hinzufügt, sofern Platz vorhanden ist - Eine
Get(int index)-Methode haben, die das Element am angegebenen Index zurückgibt - Eine
GetAll()-Methode haben, die alle gespeicherten Elemente, verbunden durch", ", zurückgibt
- Ein privates Array vom Typ
Program.cs: Demonstrieren Sie in Ihrer Hauptdatei, wie Ihre generische Klasse mit zwei verschiedenen Typen funktioniert. Erstellen Sie einStorage<int>und einStorage<string>, fügen Sie jedem Elemente hinzu und zeigen Sie, dass dieselbe Klasse nahtlos mit beiden Typen funktioniert.
Sie erhalten vier Eingaben:
- Erster zu speichernder Integer
- Zweiter zu speichernder Integer
- Erster zu speichernder String
- Zweiter zu speichernder String
Erstellen Sie einen Integer-Speicher mit der Kapazität 5 und einen String-Speicher mit der Kapazität 5. Fügen Sie die zwei Integer dem Integer-Speicher und die zwei Strings dem String-Speicher hinzu.
Geben Sie die Ausgabe in diesem Format aus:
Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}Wenn die Eingaben beispielsweise 42, 100, Hello und World sind, sollte die Ausgabe wie folgt aussehen:
Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: HelloBeachten Sie, wie Sie die Storage<T>-Klasse nur einmal schreiben, sie aber perfekt mit sowohl int als auch string funktioniert - das ist die Magie von Generics! Der Compiler gewährleistet Typsicherheit, sodass Sie nicht versehentlich einen String zu Ihrem Integer-Speicher hinzufügen können.
Spickzettel
Generics ermöglichen es Ihnen, Klassen und Methoden zu schreiben, die mit jedem Datentyp arbeiten, während die Typsicherheit gewahrt bleibt.
Eine generische Klasse verwendet einen Typparameter (typischerweise T), der in spitzen Klammern eingeschlossen ist:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}Geben Sie beim Erstellen einer Instanz den Typ an, der T ersetzen soll:
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Generische Methoden ermöglichen es einer einzelnen Methode, mit verschiedenen Typen zu arbeiten:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // Compiler leitet den Typ aus den Argumenten abGenerics bieten eine Typprüfung zur Kompilierzeit und sind die Grundlage für Sammlungen wie List<T> und Dictionary<TKey, TValue>.
Probier es selbst
using System;
using DataStorage;
class Program
{
public static void Main(string[] args)
{
// Eingaben lesen
int num1 = Convert.ToInt32(Console.ReadLine());
int num2 = Convert.ToInt32(Console.ReadLine());
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
// TODO: Erstelle einen Storage<int> mit einer Kapazität von 5
// TODO: Füge die beiden Ganzzahlen zum Ganzzahl-Speicher hinzu
// TODO: Erstelle einen Storage<string> mit einer Kapazität von 5
// TODO: Füge die beiden Zeichenfolgen zum Zeichenfolgen-Speicher hinzu
// TODO: Gib die Ausgabe im erforderlichen Format aus
// Int storage: {GetAll()}
// Int count: {Count}
// First int: {Get(0)}
// String storage: {GetAll()}
// String count: {Count}
// First string: {Get(0)}
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungDependency Injection Grundlagen3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten