Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jótanács
Új szoftverfejlesztés? Először az Első lépések oktatóanyagokkal kezdje. Amint használ olyan gyűjteményeket, mint a List<T>.
Tapasztalt egy másik nyelven? A C#-generikusok hasonlóak a Java vagy a C++ sablonjaihoz, de teljes futásidejű típusinformációkkal és típustörlés nélkül. Olvassuk át a C#-specifikus minták gyűjteménykifejezéseit és kovariancia és kontravariancia szakaszait.
A generics lehetővé teszi, hogy bármilyen típussal működő kódot írjon, miközben a teljes típusbiztonságot megtartja. Ahelyett, hogy külön osztályokat vagy metódusokat írnál a , intés minden más szükséges típushozstring, írjon egy verziót egy vagy több típusparaméterrel (például T, vagy TKeyTValue) és adja meg a tényleges típusokat a használat során. A fordító fordításkor ellenőrzi a típusokat, így nincs szükség futtatókörnyezeti leadásra vagy kockázatra InvalidCastException.
A mindennapi C#-ban folyamatosan találkozunk általánosokkal. A gyűjtemények, az aszinkron visszatérési típusok, a meghatalmazottak és a LINQ mind általános típusokra támaszkodnak:
List<int> scores = [95, 87, 72, 91];
Dictionary<string, decimal> prices = new()
{
["Widget"] = 19.99m,
["Gadget"] = 29.99m
};
Task<string> greeting = Task.FromResult("Hello, generics!");
Func<int, bool> isPositive = n => n > 0;
Console.WriteLine($"First score: {scores[0]}");
Console.WriteLine($"Widget price: {prices["Widget"]:C}");
Console.WriteLine($"Greeting: {await greeting}");
Console.WriteLine($"Is 5 positive? {isPositive(5)}");
A szögletes zárójelek (<int>, <string>, <Product>) típusargumentuma minden esetben megadja az általános típusnak, hogy milyen típusú adatokat tárol vagy használ. A fordító biztosítja a típusbiztonságot. Véletlenül nem adhat hozzá egy string elemet egy List<int> elemhez.
Általános típusok felhasználása
Gyakrabban használ általános típusokat a .NET osztálykönyvtárból ahelyett, hogy sajátot hoz létre. Az alábbi szakaszok a leggyakrabban használt általános típusokat mutatják be.
Általános gyűjtemények
A System.Collections.Generic névtér típusbiztos gyűjteményosztályokat biztosít. Mindig ezeket a gyűjteményeket használja a nemgenerikus gyűjtemények helyett, például ArrayList:
// A strongly typed list of strings
List<string> names = ["Alice", "Bob", "Carol"];
names.Add("Dave");
// names.Add(42); // Compile-time error: can't add an int to List<string>
// A dictionary mapping string keys to int values
var inventory = new Dictionary<string, int>
{
["Apples"] = 50,
["Oranges"] = 30
};
inventory["Bananas"] = 25;
// A set that prevents duplicates
HashSet<int> uniqueIds = [1, 2, 3, 1, 2];
Console.WriteLine($"Unique count: {uniqueIds.Count}"); // 3
// A FIFO queue
Queue<string> tasks = new();
tasks.Enqueue("Build");
tasks.Enqueue("Test");
Console.WriteLine($"Next task: {tasks.Dequeue()}"); // Build
Az általános gyűjtemények megakadályozzák a futásidőben felmerülő típushibákat, mert a hibák fordításkor jelennek meg. Ezek a gyűjtemények az értéktípusok boxolását is elkerülik, ami javítja a teljesítményt.
Általános metódusok
Az általános metódus saját típusparamétert deklarál. A fordító gyakran a megadott értékekből következtet a típusargumentumra, ezért nem kell explicit módon megadnia:
static void Print<T>(T value) =>
Console.WriteLine($"Value: {value}");
Print(42); // Compiler infers T as int
Print("hello"); // Compiler infers T as string
Print(3.14); // Compiler infers T as double
A hívásban Print(42), a fordító az argumentumból következtet Tint.
Print<int>(42) Explicit módon írhat, de a típus-következtetés tisztán tartja a kódot.
Gyűjteménykifejezések
A gyűjteménykifejezések (C# 12) tömör szintaxist biztosítanak a gyűjtemények létrehozásához. Konstruktorhívások vagy inicializáló szintaxis helyett használjon szögletes zárójeleket:
// Create a list with a collection expression
List<string> fruits = ["Apple", "Banana", "Cherry"];
// Create an array
int[] numbers = [1, 2, 3, 4, 5];
// Works with any supported collection type
IReadOnlyList<double> temperatures = [72.0, 68.5, 75.3];
Console.WriteLine($"Fruits: {string.Join(", ", fruits)}");
Console.WriteLine($"Numbers: {string.Join(", ", numbers)}");
Console.WriteLine($"Temps: {string.Join(", ", temperatures)}");
A spread operátor (..) az egyik gyűjtemény elemeit egy másikba illeszti, ami a sorozatok kombinálására hasznos:
List<int> first = [1, 2, 3];
List<int> second = [4, 5, 6];
// Spread both lists into a new combined list
List<int> combined = [.. first, .. second];
Console.WriteLine(string.Join(", ", combined));
// Output: 1, 2, 3, 4, 5, 6
// Add extra elements alongside spreads
List<int> withExtras = [0, .. first, 99, .. second];
Console.WriteLine(string.Join(", ", withExtras));
// Output: 0, 1, 2, 3, 99, 4, 5, 6
A gyűjteménykifejezések tömbökkel, List<T>, Span<T>, ImmutableArray<T>és a gyűjteménykészítő mintát támogató bármilyen típussal működnek. A szintaxis teljes referenciáját a Gyűjtemény kifejezések című témakörben talál.
Szótár inicializálása
A szótárakat tömören inicializálhatja indexelő inicializálókkal. Ez a szintaxis szögletes zárójelekkel állítja be a kulcs-érték párokat:
Dictionary<string, int> scores = new()
{
["Alice"] = 95,
["Bob"] = 87,
["Carol"] = 92
};
foreach (var (name, score) in scores)
{
Console.WriteLine($"{name}: {score}");
}
Összevonhatja a szótárakat egy másolat készítésével és felülbírálások alkalmazásával:
Dictionary<string, int> defaults = new()
{
["Timeout"] = 30,
["Retries"] = 3
};
Dictionary<string, int> overrides = new()
{
["Timeout"] = 60
};
// Merge defaults and overrides into a new dictionary
Dictionary<string, int> config = new(defaults);
foreach (var (key, value) in overrides)
{
config[key] = value;
}
Console.WriteLine($"Timeout: {config["Timeout"]}"); // 60
Console.WriteLine($"Retries: {config["Retries"]}"); // 3
Típuskorlátozások
A korlátozások korlátozzák, hogy az általános típus vagy metódus mely típusargumentumokat fogadja el. A korlátozások lehetővé teszik olyan metódusok vagy hozzáférési tulajdonságok meghívását a típusparaméteren object , amelyek egyedül nem lennének elérhetők:
static T Max<T>(T a, T b) where T : IComparable<T> =>
a.CompareTo(b) >= 0 ? a : b;
Console.WriteLine(Max(3, 7)); // 7
Console.WriteLine(Max("apple", "banana")); // banana
static T CreateDefault<T>() where T : new() => new T();
var list = CreateDefault<List<int>>(); // Creates an empty List<int>
Console.WriteLine($"Empty list count: {list.Count}"); // 0
A leggyakoribb korlátozások a következők:
| Kényszer | Meaning |
|---|---|
where T : class |
T referenciatípusnak kell lennie |
where T : struct |
T nem null értékű értéktípusnak kell lennie |
where T : new() |
T nyilvános paraméter nélküli konstruktorsal kell rendelkeznie |
where T : BaseClass |
T származnia kell a BaseClass |
where T : IInterface |
T implementálnia kell IInterface |
A kényszerek kombinálhatók: where T : class, IComparable<T>, new(). A kevésbé gyakori korlátozások közé tartozik a where T : System.Enum, a where T : System.Delegate, és a where T : unmanaged a speciális esetekben. A teljes listát a típusparaméterekre vonatkozó korlátozások című témakörben találja.
Kovariancia és ellenvariancia
A kovariancia és a contravariance azt írja le, hogyan viselkednek az általános típusok az örökléssel. Ezek határozzák meg, hogy használható-e az eredetileg megadottnál származtatottabb vagy kevésbé származtatott típusargumentum:
// Covariance: IEnumerable<Dog> can be used as IEnumerable<Animal>
// because IEnumerable<out T> is covariant
List<Dog> dogs = [new("Rex"), new("Buddy")];
IEnumerable<Animal> animals = dogs; // Allowed because Dog derives from Animal
foreach (var animal in animals)
{
Console.WriteLine(animal.Name);
}
// Contravariance: Action<Animal> can be used as Action<Dog>
// because Action<in T> is contravariant
Action<Animal> printAnimal = a => Console.WriteLine($"Animal: {a.Name}");
Action<Dog> printDog = printAnimal; // Allowed because any Animal handler can handle Dog
printDog(new Dog("Spot"));
-
Kovariancia (
out T): AzIEnumerable<Dog>ott használható, aholIEnumerable<Animal>várható, mertDogAnimal-ből származik. Eztouta típusparaméter kulcsszója teszi lehetővé. A covariant típusú paraméterek csak kimeneti pozíciókban (visszatérési típusok) jelenhetnek meg. -
Contravariance (
in T): EgyAction<Animal>használható ott, aholAction<Dog>várható, mert bármely kezelő, amely képes kezelniAnimal-et, kezelni tudjaDog-t is. Ezt ainkulcsszó teszi lehetővé. A contravariant típusú paraméterek csak bemeneti pozíciókban (paraméterekben) jelenhetnek meg.
Számos beépített felület és delegátumok már variáns: IEnumerable<out T>, IReadOnlyList<out T>, Func<out TResult>, és Action<in T>. Az ilyen típusok használatakor automatikusan kihasználhatja a varianciát. A variáns felületek és delegáltak tervezésének részletes kezeléséről lásd: Kovariancia és ellenvariancia.
Saját általános típusok létrehozása
Saját általános osztályokat, szerkezeteket, interfészeket és metódusokat határozhat meg. Az alábbi példa egy egyszerű, általános csatolt listát mutat be az ábrához. A gyakorlatban használjon List<T> vagy más beépített gyűjteményt:
public class GenericList<T>
{
private class Node(T data)
{
public T Data { get; set; } = data;
public Node? Next { get; set; }
}
private Node? head;
public void AddHead(T data)
{
var node = new Node(data) { Next = head };
head = node;
}
public IEnumerator<T> GetEnumerator()
{
var current = head;
while (current is not null)
{
yield return current.Data;
current = current.Next;
}
}
}
var list = new GenericList<int>();
for (var i = 0; i < 5; i++)
{
list.AddHead(i);
}
foreach (var item in list)
{
Console.Write($"{item} ");
}
Console.WriteLine();
// Output: 4 3 2 1 0
Az általános típusok nem korlátozódnak az osztályokra. Általános interface, structés record típusokat is definiálhat. Az általános algoritmusok és az összetett kényszerkombinációk tervezésével kapcsolatos további információkért lásd: Generics in .NET.