Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Tupel bieten eine einfache Datenstruktur für mehrere Elemente in einer einzigen Struktur. Sie sind die bevorzugte Wahl gegenüber anonymen Typen. Tupel bieten eine bessere Leistung, unterstützen die Dekonstruktion und bieten eine flexiblere Syntax.
Anonyme Typen stellen eine praktische Möglichkeit dar, einen Satz schreibgeschützter Eigenschaften in einem Objekt zu kapseln, ohne zuerst explizit einen Typ definieren zu müssen. Der Compiler generiert den Typnamen und ist auf Quellcodeebene nicht verfügbar. Der Compiler leitet den Typ jeder Eigenschaft ab. Verwenden Sie anonyme Typen in erster Linie, wenn Sie Unterstützung für die Ausdrucksstruktur benötigen oder wenn Sie mit Code arbeiten, der Referenztypen erfordert.
Tupel im Vergleich zu anonymen Typen
Mit Tupeln und anonymen Typen können Sie mehrere Werte gruppieren, ohne einen benannten Typ zu definieren. Tupel verfügen jedoch über eine bessere Sprachunterstützung und kompilieren in einer effizienteren Datenstruktur. In der folgenden Tabelle sind die wichtigsten Unterschiede zusammengefasst:
| Merkmal | Anonyme Typen | Tupel |
|---|---|---|
| Typ | Referenztyp (class) |
Werttyp (struct) |
| Leistung | Heap-Zuordnung | Stapelzuordnung (bessere Leistung) |
| Mutability | Schreibgeschützte Eigenschaften | Veränderbare Felder |
| Dekonstruktion | Nicht unterstützt | Unterstützt |
| Ausdrucksbaumstrukturen | Unterstützt | Nicht unterstützt |
| Zugriffsmodifizierer | internal |
public |
| Mitgliedernamen | Erforderlich oder abgeleitet | Optional (mit Standardnamen wie Item1, Item2) |
Wann man Tupel verwenden sollte
Verwenden Sie Tupel, wenn:
- Sie benötigen eine bessere Leistung durch Stapelallokation.
- Sie möchten Werte in separate Variablen zerlegen.
- Sie geben mehrere Werte aus einer Methode zurück.
- Sie benötigen keine Unterstützung für Ausdrucksbäume.
Das folgende Beispiel zeigt, wie Tupel ähnliche Funktionalität wie anonyme Typen mit einer klareren Syntax bereitstellen.
// Tuple with named elements.
var tupleProduct = (Name: "Widget", Price: 19.99M);
Console.WriteLine($"Tuple: {tupleProduct.Name} costs ${tupleProduct.Price}");
// Equivalent example using anonymous types.
var anonymousProduct = new { Name = "Widget", Price = 19.99M };
Console.WriteLine($"Anonymous: {anonymousProduct.Name} costs ${anonymousProduct.Price}");
Tupeldestruktion
Sie können ein Tupel in separate Variablen zerlegen, was eine bequeme Möglichkeit zum Arbeiten mit den einzelnen Elementen eines Tupels bietet. C# unterstützt verschiedene Möglichkeiten zum Dekonstruieren von Tupeln.
static (string Name, int Age, string City) GetPersonInfo()
{
return ("Alice", 30, "Seattle");
}
// Deconstruct using var for all variables
var (name, age, city) = GetPersonInfo();
Console.WriteLine($"{name} is {age} years old and lives in {city}");
// Output: Alice is 30 years old and lives in Seattle
// Deconstruct with explicit types
(string personName, int personAge, string personCity) = GetPersonInfo();
Console.WriteLine($"{personName}, {personAge}, {personCity}");
// Deconstruct into existing variables
string existingName;
int existingAge;
string existingCity;
(existingName, existingAge, existingCity) = GetPersonInfo();
// Deconstruct and discard unwanted values using the discard pattern (_)
var (name2, _, city2) = GetPersonInfo();
Console.WriteLine($"{name2} lives in {city2}");
// Output: Alice lives in Seattle
Deconstruction ist in Schleifen und Szenarien zur Mustererkennung nützlich:
var people = new List<(string Name, int Age)>
{
("Bob", 25),
("Carol", 35),
("Dave", 40)
};
foreach (var (personName2, personAge2) in people)
{
Console.WriteLine($"{personName2} is {personAge2} years old");
}
Tupel als Methodenrückgabetyp
Ein gängiger Anwendungsfall für Tupel ist ein Methodenrückgabetyp. Anstatt Parameter zu definieren out , können Sie die Methodenergebnisse in einem Tupel gruppieren. Sie können keinen anonymen Typ aus einer Methode zurückgeben, da er keinen Namen hat und der Rückgabetyp nicht deklariert werden kann.
Das folgende Beispiel veranschaulicht die Verwendung von Tupeln beim Nachschlagen im Wörterbuch, um Konfigurationsbereiche zurückzugeben.
var configLookup = new Dictionary<int, (int Min, int Max)>()
{
[2] = (4, 10),
[4] = (10, 20),
[6] = (0, 23)
};
if (configLookup.TryGetValue(4, out (int Min, int Max) range))
{
Console.WriteLine($"Found range: min is {range.Min}, max is {range.Max}");
}
// Output: Found range: min is 10, max is 20
Dieses Muster ist hilfreich beim Arbeiten mit Methoden, die sowohl einen Erfolgsindikator als auch mehrere Ergebniswerte zurückgeben müssen. Mit dem Tupel können Sie benannte Felder (Min und Max) anstelle von generischen Namen wie Item1 und Item2verwenden, wodurch der Code besser lesbar und selbst dokumentiert wird.
Wann anonyme Typen verwendet werden sollen
Anonyme Typen verwenden, wenn:
- Sie arbeiten mit Ausdrucksbäumen (z. B. in einigen Microsoft Language-Integrated Query (LINQ)-Anbietern).
- Das Objekt muss ein Verweistyp sein.
Das gängigste Szenario ist die Initialisierung eines anonymen Typs mit den Eigenschaften eines anderen Typs. Nehmen Sie im folgenden Beispiel an, dass eine Klasse mit dem Namen Product vorhanden ist. Klasse Product enthält Color und Price Eigenschaften zusammen mit anderen Eigenschaften, die Sie nicht interessieren:
class Product
{
public string? Color { get; init; }
public decimal Price { get; init; }
public string? Name { get; init; }
public string? Category { get; init; }
public string? Size { get; init; }
}
Die anonyme Typdeklaration beginnt mit dem new Operator zusammen mit einem Objektinitialisierer. Die Deklaration initialisiert einen neuen Typ, der nur zwei Eigenschaften von Product verwendet. Anonyme Typen werden in der Regel in der select Klausel eines Abfrageausdrucks verwendet, um eine kleinere Datenmenge zurückzugeben. Weitere Informationen zu Abfragen finden Sie unter LINQ in C#.
Wenn Sie keine Membernamen im anonymen Typ angeben, vergibt der Compiler den Mitgliedern des anonymen Typs denselben Namen wie der Eigenschaft, mit der sie initialisiert werden. Sie geben einen Namen für eine Eigenschaft an, die mit einem Ausdruck initialisiert wird, wie im vorherigen Beispiel dargestellt.
Im folgenden Beispiel lauten die Namen der Eigenschaften des anonymen Typs Color und Price. Bei den Instanzen handelt es sich um Elemente aus der products Sammlung von Product Typen:
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
Projektionsinitialisierer mit anonymen Typen
Anonyme Typen unterstützen Projektionsinitialisierer, mit denen Sie lokale Variablen oder Parameter direkt verwenden können, ohne den Membernamen explizit anzugeben. Der Compiler leitet die Membernamen aus den Variablennamen ab. Im folgenden Beispiel wird diese vereinfachte Syntax veranschaulicht:
// Explicit member names.
var personExplicit = new { FirstName = "Kyle", LastName = "Mit" };
// Projection initializers (inferred member names).
var firstName = "Kyle";
var lastName = "Mit";
var personInferred = new { firstName, lastName };
// Both create equivalent anonymous types with the same property names.
Console.WriteLine($"Explicit: {personExplicit.FirstName} {personExplicit.LastName}");
Console.WriteLine($"Inferred: {personInferred.firstName} {personInferred.lastName}");
Diese vereinfachte Syntax ist beim Erstellen anonymer Typen mit vielen Eigenschaften nützlich:
var title = "Software Engineer";
var department = "Engineering";
var salary = 75000;
// Using projection initializers.
var employee = new { title, department, salary };
// Equivalent to explicit syntax:
// var employee = new { title = title, department = department, salary = salary };
Console.WriteLine($"Title: {employee.title}, Department: {employee.department}, Salary: {employee.salary}");
Der Membername wird in den folgenden Fällen nicht abgeleitet:
- Der Kandidatname dupliziert ein anderes Eigenschaftselement im gleichen anonymen Typ, entweder explizit oder implizit.
- Der Kandidatname ist kein gültiger Bezeichner (z. B. enthält Leerzeichen oder Sonderzeichen).
In diesen Fällen müssen Sie den Membernamen explizit angeben.
Tipp
Sie können die .NET-Stilregel IDE0037 verwenden, um festzulegen, ob abgeleitete oder explizite Membernamen bevorzugt werden.
Sie können ein Feld auch mithilfe eines Objekts eines anderen Typs definieren: Klasse, Struktur oder sogar einen anderen anonymen Typ. Verwenden Sie dazu die Variable, die dieses Objekt enthält. Das folgende Beispiel zeigt zwei anonyme Typen, die bereits instanziierte benutzerdefinierte Typen verwenden. In beiden Fällen ist das product Feld in den anonymen Typen shipment vom Typ shipmentWithBonus und Product enthält die Standardwerte jedes Felds. Das bonus Feld weist einen anonymen Typ auf, der vom Compiler erstellt wurde.
var product = new Product();
var bonus = new { note = "You won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "Somewhere St.", product, bonus };
Wenn Sie einen anonymen Typ zum Initialisieren einer Variablen verwenden, deklarieren Sie die Variable normalerweise mithilfe von var als implizit typisierte lokale Variable. Sie können den Typnamen in der Variablendeklaration nicht angeben, da nur der Compiler Zugriff auf den zugrunde liegenden Namen des anonymen Typs hat. Weitere Informationen zu var finden Sie unter Implizit typisierte lokale Variablen.
Sie können ein Array anonym typisierter Elemente erstellen, indem Sie eine implizit typisierte lokale Variable und ein implizit typisiertes Array kombinieren, wie im folgenden Beispiel veranschaulicht.
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
Anonyme Typen sind class Typen, die direkt von object abgeleitet werden, und Sie können sie keinem anderen Typ zuweisen außer object. Der Compiler stellt einen Namen für jeden anonymen Typ bereit, obwohl ihre Anwendung nicht darauf zugreifen kann. Aus der Perspektive der Common Language Runtime unterscheidet sich ein anonymer Typ nicht von anderen Verweistypen.
Wenn zwei oder mehr anonyme Objektinitialisierer in einer Assembly eine Sequenz von Eigenschaften angeben, die die gleiche Reihenfolge und die gleichen Namen und Typen aufweisen, behandelt der Compiler die Objekte als Instanzen desselben Typs. Sie erhalten die gleichen vom Compiler generierten Typinformationen.
Anonyme Typen unterstützen eine nicht destruktive Mutation in Form von with-Ausdrücken. Mit diesem Feature können Sie eine neue Instanz eines anonymen Typs erstellen, bei der mindestens eine Eigenschaft neue Werte aufweist:
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);
Sie können kein Feld, eine Eigenschaft, ein Ereignis oder den Rückgabetyp einer Methode als anonymen Typ deklarieren. Ebenso können Sie keinen formalen Parameter einer Methode, einer Eigenschaft, eines Konstruktors oder eines Indexers als anonymen Typ deklarieren. Um einen anonymen Typ oder eine Auflistung mit anonymen Typen als Argument einer Methode zu übergeben, können Sie den Parameter als object-Typ deklarieren. Die Verwendung von object für anonyme Typen verfehlt jedoch den Zweck der starken Typisierung. Wenn Sie Abfrageergebnisse speichern oder außerhalb der Methodengrenze übergeben müssen, könnten Sie statt eines anonymen Typs eine Struktur oder Klasse mit einem gewöhnlichen Namen verwenden.
Da die Methoden Equals und GetHashCode von anonymen Typen anhand der Methoden Equals und GetHashCode der Eigenschaften definiert werden, sind zwei Instanzen eines anonymen Typs nur gleich, wenn alle Eigenschaften gleich sind.
Hinweis
Die Barrierefreiheitsstufe eines anonymen Typs lautet internal. Daher sind zwei anonyme Typen, die in unterschiedlichen Assemblys definiert sind, nicht vom gleichen Typ.
Daher können Instanzen anonymer Typen nicht gleicheinander sein, wenn sie in verschiedenen Assemblys definiert sind, auch wenn alle ihre Eigenschaften gleich sind.
Anonyme Typen überschreiben die ToString-Methode und verketten den Namen und die ToString-Ausgabe jeder Eigenschaft, die in geschweiften Klammern steht.
var v = new { Title = "Hello", Age = 24 };
Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"