Delen via


Tuples en anonieme typen

Tuples bieden een lichtgewicht gegevensstructuur voor meerdere leden in één structuur. Ze zijn de voorkeurskeuze ten opzichte van anonieme typen. Tuples bieden betere prestaties, ondersteuning voor deconstructie en bieden flexibelere syntaxis.

Anonieme typen bieden een handige manier om een set alleen-lezeneigenschappen in te kapselen in één object zonder dat u eerst expliciet een type hoeft te definiëren. De compiler genereert de typenaam en is niet beschikbaar op broncodeniveau. De compiler bepaalt het type van elke eigenschap. Gebruik anonieme typen voornamelijk wanneer u ondersteuning voor expressiestructuren nodig hebt of wanneer u werkt met code waarvoor referentietypen zijn vereist.

Tuples versus anonieme typen

Met zowel tuples als anonieme typen kunt u meerdere waarden groeperen zonder een benoemd type te definiëren. Tuples hebben echter betere taalondersteuning en compileren naar een efficiëntere gegevensstructuur. De volgende tabel bevat een overzicht van de belangrijkste verschillen:

Eigenschap Anonieme typen Tupels
Typologie Verwijzingstype (class) Waardetype (struct)
Performance Heap-toewijzing Stacktoewijzing (betere prestaties)
Mutability Eigenschappen met het kenmerk Alleen-lezen Onveranderbare velden
Deconstructie Niet ondersteund Ondersteund
Expressiebomen Ondersteund Niet ondersteund
Toegangsmodifier internal public
Ledennamen Vereist of afgeleid Optioneel (met standaardnamen zoals Item1, Item2)

Wanneer gebruikt u tuples?

Gebruik tuples wanneer:

  • U hebt betere prestaties nodig via stacktoewijzing.
  • U wilt waarden deconstrueren in afzonderlijke variabelen.
  • U retourneert meerdere waarden uit een methode.
  • U hebt geen ondersteuning voor expression trees nodig.

In het volgende voorbeeld ziet u hoe tuples vergelijkbare functionaliteit bieden als anonieme typen met een schonere syntaxis:

// 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}");

Tupledeconstructie

U kunt een tuple deconstrueren in afzonderlijke variabelen, wat een handige manier biedt om met afzonderlijke tuple-elementen te werken. C# ondersteunt verschillende manieren om tuples te deconstrueren:

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

Deconstructie is handig in lussen en patroonherkenningsscenario's.

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");
}

Tuples als methode-retourtype

Een veelvoorkomend gebruiksvoorbeeld voor tuples is als methode-retourtype. In plaats van parameters te definiëren out , kunt u de resultaten van de methode groeperen in een tuple. U kunt geen anoniem type retourneren vanuit een methode, omdat het geen naam heeft en het retourtype niet kan worden gedeclareerd.

In het volgende voorbeeld ziet u hoe tuples te gebruiken met woordenboekopzoekingen om configuratiebereiken te retourneren.

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

Dit patroon is handig bij het werken met methoden die zowel een succesindicator als meerdere resultaatwaarden moeten retourneren. Met de tuple kunt u benoemde velden (Min en Max) gebruiken in plaats van algemene namen, zoals Item1 en Item2, waardoor de code beter leesbaar en zelfdocumenterend wordt.

Wanneer anonieme typen gebruiken

Anonieme typen gebruiken wanneer:

  • U werkt met expressiebomen (bijvoorbeeld in sommige providers van Microsoft Language-Integrated Query (LINQ).
  • U wilt dat het object een verwijzingstype is.

Het meest voorkomende scenario is het initialiseren van een anoniem type met eigenschappen van een ander type. In het volgende voorbeeld wordt ervan uitgegaan dat er een klasse bestaat met de naam Product. Klasse Product bevat Color en Price eigenschappen, samen met andere eigenschappen waarin u niet geïnteresseerd bent:

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; }
}

De declaratie van het anonieme type begint met de new operator samen met een object-initialisatiefunctie. De declaratie initialiseert een nieuw type dat slechts twee eigenschappen van Product. Anonieme typen worden doorgaans gebruikt in de select component van een query-expressie om een kleinere hoeveelheid gegevens te retourneren. Zie LINQ in C# voor meer informatie over query's.

Als u geen lidnamen opgeeft in het anonieme type, geeft de compiler de anonieme typeleden dezelfde naam als de eigenschap die wordt gebruikt om ze te initialiseren. U geeft een naam op voor een eigenschap die wordt geïnitialiseerd met een expressie, zoals wordt weergegeven in het vorige voorbeeld.

In het volgende voorbeeld zijn Color de namen van de eigenschappen van het anonieme type en Price. De exemplaren zijn items uit de products verzameling typen Product :

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);
}

Initializers voor projecties met anonieme typen

Anonieme typen bieden ondersteuning voor projectie-initializers, waarmee u lokale variabelen of parameters rechtstreeks kunt gebruiken zonder expliciet de lidnaam op te geven. De compiler leidt de leden af van de namen van de variabelen. In het volgende voorbeeld ziet u deze vereenvoudigde syntaxis:

// 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}");

Deze vereenvoudigde syntaxis is handig bij het maken van anonieme typen met veel eigenschappen:

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}");

De lidnaam wordt niet afgeleid in de volgende gevallen:

  • De naam van de kandidaat dupliceert een ander eigenschapslid in hetzelfde anonieme type, expliciet of impliciet.
  • De naam van de kandidaat is geen geldige id (deze bevat bijvoorbeeld spaties of speciale tekens).

In deze gevallen moet u expliciet de lidnaam opgeven.

Aanbeveling

U kunt .NET-stijlregel IDE0037 gebruiken om af te dwingen of afgeleid of expliciete ledennamen de voorkeur hebben.

U kunt ook een veld definiëren met behulp van een object van een ander type: klasse, struct of zelfs een ander anoniem type. Gebruik hiervoor de variabele die dit object bevat. In het volgende voorbeeld ziet u twee anonieme typen die al door de gebruiker gedefinieerde typen gebruiken. In beide gevallen is het veld product in de anonieme typen shipment en shipmentWithBonus van het type Product en bevat het de standaardwaarden van elk veld. Het bonus veld is van een anoniem type dat door de compiler is gemaakt.

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 };

Wanneer u een anoniem type gebruikt om een variabele te initialiseren, declareert u de variabele doorgaans als een impliciet getypte lokale variabele met behulp van var. U kunt de typenaam in de variabeledeclaratie niet opgeven omdat alleen de compiler toegang heeft tot de onderliggende naam van het anonieme type. Zie Impliciet getypte lokale variabelen voor meer informatie over.var

U kunt een matrix met anoniem getypte elementen maken door een impliciet getypte lokale variabele en een impliciet getypte matrix te combineren, zoals wordt weergegeven in het volgende voorbeeld.

var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};

Anonieme typen zijn class typen die rechtstreeks zijn afgeleid van object, en u kunt ze niet casten naar een willekeurig type, behalve object. De compiler biedt een naam voor elk anoniem type, hoewel uw toepassing er geen toegang toe heeft. Vanuit het perspectief van de algemene taalruntime is een anoniem type niet anders dan elk ander referentietype.

Als twee of meer anonieme object initializers in een assembly een reeks eigenschappen opgeven die zich in dezelfde volgorde bevinden en dezelfde namen en typen hebben, behandelt de compiler de objecten als exemplaren van hetzelfde type. Ze delen dezelfde door compiler gegenereerde typegegevens.

Anonieme typen ondersteunen niet-destructieve mutatie in de vorm van expressies. Met deze functie kunt u een nieuw exemplaar van een anoniem type maken waarbij een of meer eigenschappen nieuwe waarden hebben:

var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);

U kunt een veld, een eigenschap, een gebeurtenis of het retourtype van een methode niet declareren als een anoniem type. Op dezelfde manier kunt u geen formele parameter van een methode, eigenschap, constructor of indexeerfunctie declareren als een anoniem type. Als u een anoniem type of een verzameling wilt doorgeven die anonieme typen bevat, kunt u als argument voor een methode de parameter declareren als type object. Het gebruik object voor anonieme typen verslaat echter het doel van sterk typen. Als u queryresultaten moet opslaan of deze buiten de methodegrens moet doorgeven, kunt u overwegen een gewone benoemde struct of klasse te gebruiken in plaats van een anoniem type.

Omdat de Equals en GetHashCode methoden voor anonieme typen worden gedefinieerd in termen van de Equals en GetHashCode methoden van de eigenschappen, zijn twee exemplaren van hetzelfde anonieme type alleen gelijk als alle eigenschappen gelijk zijn.

Notitie

Het toegankelijkheidsniveau van een anoniem type is internal. Daarom zijn twee anonieme typen die in verschillende assembly's zijn gedefinieerd, niet van hetzelfde type. Daarom kunnen exemplaren van anonieme typen niet gelijk zijn aan elkaar wanneer ze zijn gedefinieerd in verschillende assembly's, zelfs niet wanneer alle eigenschappen gelijk zijn.

Anonieme typen overschrijven de ToString methode, waarbij de naam en ToString uitvoer van elke eigenschap tussen accolades worden samengevoegd.

var v = new { Title = "Hello", Age = 24 };

Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"

Zie ook