Delen via


Tuple-typen (C#-verwijzing)

De tuples-functie biedt een beknopte syntaxis voor het groeperen van meerdere gegevenselementen in een lichtgewicht gegevensstructuur.

De C#-taalreferentiedocumenten de laatst uitgebrachte versie van de C#-taal. Het bevat ook de eerste documentatie voor functies in openbare previews voor de aanstaande taalrelease.

De documentatie identificeert alle functies die voor het eerst zijn geïntroduceerd in de laatste drie versies van de taal of in de huidige openbare previews.

Tip

Raadpleeg het artikel over de versiegeschiedenis van de C#-taal om te achterhalen wanneer een functie voor het eerst is geïntroduceerd in C#.

In het volgende voorbeeld ziet u hoe u een tuplevariabele kunt declareren, initialiseren en toegang kunt krijgen tot de gegevensleden:

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

Zoals in het voorgaande voorbeeld wordt weergegeven, geeft u de typen van alle gegevensleden en eventueel de veldnamen op om een tuple-type te definiëren. U kunt geen methoden definiëren in een tupletype, maar u kunt de methoden van .NET gebruiken, zoals in het volgende voorbeeld wordt weergegeven:

(double, int) t = (4.5, 3);
Console.WriteLine(t.ToString());
Console.WriteLine($"Hash code of {t} is {t.GetHashCode()}.");
// Output:
// (4.5, 3)
// Hash code of (4.5, 3) is 718460086.

Tuple-typen ondersteunen gelijkheidsoperators== en !=. Zie de sectie Tuple-gelijkheid voor meer informatie.

Tuple-typen zijn waardetypen; tuple-elementen zijn openbare velden. Dit ontwerp maakt tuples veranderlijke waardetypen.

U kunt tuples definiëren met een willekeurig groot aantal elementen:

var t =
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26);
Console.WriteLine(t.Item26);  // output: 26

Gebruiksvoorbeelden voor tuples

Een van de meest voorkomende gebruiksvoorbeelden voor tuples is als retourtype methode. In plaats van methodeparameters te definiërenout, resulteert de groepsmethode in een tuple-retourtype, zoals in het volgende voorbeeld wordt weergegeven:

int[] xs = new int[] { 4, 7, 9 };
var limits = FindMinMax(xs);
Console.WriteLine($"Limits of [{string.Join(" ", xs)}] are {limits.min} and {limits.max}");
// Output:
// Limits of [4 7 9] are 4 and 9

int[] ys = new int[] { -9, 0, 67, 100 };
var (minimum, maximum) = FindMinMax(ys);
Console.WriteLine($"Limits of [{string.Join(" ", ys)}] are {minimum} and {maximum}");
// Output:
// Limits of [-9 0 67 100] are -9 and 100

(int min, int max) FindMinMax(int[] input)
{
    if (input is null || input.Length == 0)
    {
        throw new ArgumentException("Cannot find minimum and maximum of a null or empty array.");
    }

    // Initialize min to MaxValue so every value in the input
    // is less than this initial value.
    var min = int.MaxValue;
    // Initialize max to MinValue so every value in the input
    // is greater than this initial value.
    var max = int.MinValue;
    foreach (var i in input)
    {
        if (i < min)
        {
            min = i;
        }
        if (i > max)
        {
            max = i;
        }
    }
    return (min, max);
}

Zoals in het voorgaande voorbeeld wordt weergegeven, kunt u rechtstreeks met het geretourneerde tuple-exemplaar werken of deze in afzonderlijke variabelen deconstrueren .

U kunt ook tuple-typen gebruiken in plaats van anonieme typen, bijvoorbeeld in LINQ-query's. Zie Kiezen tussen anonieme typen en tuples voor meer informatie.

Gebruik doorgaans tuples om losjes gerelateerde gegevenselementen te groeperen. In openbare API's kunt u overwegen om een klasse of een structuurtype te definiëren.

Tuple-veldnamen

U geeft expliciet tuple-veldnamen op in een tuple-initialisatie-expressie of in de definitie van een tuple-type, zoals in het volgende voorbeeld wordt weergegeven:

var t = (Sum: 4.5, Count: 3);
Console.WriteLine($"Sum of {t.Count} elements is {t.Sum}.");

(double Sum, int Count) d = (4.5, 3);
Console.WriteLine($"Sum of {d.Count} elements is {d.Sum}.");

Als u geen veldnaam opgeeft, kan de compiler deze afleiden uit de naam van de bijbehorende variabele in een tuple-initialisatie-expressie, zoals in het volgende voorbeeld wordt weergegeven:

var sum = 4.5;
var count = 3;
var t = (sum, count);
Console.WriteLine($"Sum of {t.count} elements is {t.sum}.");

Deze functie wordt tupleprojectie-initializers genoemd. De naam van een variabele wordt in de volgende gevallen niet geprojecteerd op een tuple-veldnaam:

  • De naam van de kandidaat is een lidnaam van een tuple-type, bijvoorbeeld Item3, ToStringof Rest.
  • De naam van de kandidaat is een duplicaat van een andere tuple-veldnaam, expliciet of impliciet.

In de voorgaande gevallen geeft u expliciet de naam van een veld op of opent u een veld met de standaardnaam.

De standaardnamen van tuple-velden zijn Item1, Item2, Item3enzovoort. U kunt altijd de standaardnaam van een veld gebruiken, zelfs wanneer een veldnaam expliciet of afgeleid is, zoals in het volgende voorbeeld wordt weergegeven:

var a = 1;
var t = (a, b: 2, 3);
Console.WriteLine($"The 1st element is {t.Item1} (same as {t.a}).");
Console.WriteLine($"The 2nd element is {t.Item2} (same as {t.b}).");
Console.WriteLine($"The 3rd element is {t.Item3}.");
// Output:
// The 1st element is 1 (same as 1).
// The 2nd element is 2 (same as 2).
// The 3rd element is 3.

Tuple-toewijzing en tuple-gelijkheidsvergelijkingen houden geen rekening met veldnamen.

Tijdens het compileren vervangt de compiler niet-standaardveldnamen door de bijbehorende standaardnamen. Als gevolg hiervan zijn expliciet opgegeven of uitgestelde veldnamen niet beschikbaar tijdens runtime.

Tip

Schakel de .NET-codestijlregel in IDE0037 om een voorkeur in te stellen voor uitgestelde of expliciete tuple-veldnamen.

Vanaf C# 12 kunt u een alias opgeven voor een tuple-type met een using instructie. In het volgende voorbeeld wordt een global using alias toegevoegd voor een tupletype met twee gehele getallen voor een toegestane Min waarde en Max waarde:

global using BandPass = (int Min, int Max);

Nadat u de alias hebt aangegeven, kunt u de BandPass naam als alias voor dat tuple-type gebruiken:

BandPass bracket = (40, 100);
Console.WriteLine($"The bandpass filter is {bracket.Min} to {bracket.Max}");

Een alias introduceert geen nieuw type, maar maakt alleen een synoniem voor een bestaand type. U kunt een tuple deconstrueren die is gedeclareerd met de BandPass alias die hetzelfde is als met het onderliggende tupletype:

(int a , int b) = bracket;
Console.WriteLine($"The bracket is {a} to {b}");

Net als bij tupletoewijzing of deconstructie hoeven de namen van de tuple-leden niet overeen te komen; de typen wel.

Op dezelfde manier kan een tweede alias met dezelfde arity- en lidtypen door elkaar worden gebruikt met de oorspronkelijke alias. U kunt een tweede alias declareren:

using Range = (int Minimum, int Maximum);

U kunt een Range tuple toewijzen aan een BandPass tuple. Net als bij alle tupletoewijzingen moeten de veldnamen niet overeenkomen, alleen de typen en de arity.

Range r = bracket;
Console.WriteLine($"The range is {r.Minimum} to {r.Maximum}");

Een alias voor een tupletype biedt meer semantische informatie wanneer u tuples gebruikt. Er wordt geen nieuw type geïntroduceerd. Als u typeveiligheid wilt bieden, moet u in plaats daarvan een positioneel record declareren.

Tuple-toewijzing en -deconstructie

C# ondersteunt toewijzing tussen tuple-typen die aan beide van de volgende voorwaarden voldoen:

  • Beide tupletypen hebben hetzelfde aantal elementen.
  • Voor elke tuplepositie is het type van het rechter tupelelement hetzelfde als of impliciet converteerbaar naar het type van het bijbehorende linker tuple-element.

Wijs tuple-elementwaarden toe door de volgorde van tuple-elementen te volgen. Het toewijzingsproces negeert de namen van tuple-velden, zoals in het volgende voorbeeld wordt weergegeven:

(int, double) t1 = (17, 3.14);
(double First, double Second) t2 = (0.0, 1.0);
t2 = t1;
Console.WriteLine($"{nameof(t2)}: {t2.First} and {t2.Second}");
// Output:
// t2: 17 and 3.14

(double A, double B) t3 = (2.0, 3.0);
t3 = t2;
Console.WriteLine($"{nameof(t3)}: {t3.A} and {t3.B}");
// Output:
// t3: 17 and 3.14

Gebruik de toewijzingsoperator = om een tuple-exemplaar in afzonderlijke variabelen te deconstrueren . U kunt dat op veel manieren doen:

  • Gebruik het var trefwoord buiten de haakjes om impliciet getypte variabelen te declareren en laat de compiler hun typen afleiden:

    var t = ("post office", 3.6);
    var (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • Declareer expliciet het type van elke variabele tussen haakjes:

    var t = ("post office", 3.6);
    (string destination, double distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • Declareer expliciet en andere typen impliciet (met) vartussen de haakjes:

    var t = ("post office", 3.6);
    (var destination, double distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • Bestaande variabelen gebruiken:

    var destination = string.Empty;
    var distance = 0.0;
    
    var t = ("post office", 3.6);
    (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    

Het doel van een deconstruct-expressie kan zowel bestaande variabelen als variabelen bevatten die zijn gedeclareerd in de destructiedeclaratie.

U kunt ook deconstructie combineren met patroonkoppeling om de kenmerken van velden in een tuple te inspecteren. In het volgende voorbeeld worden verschillende gehele getallen doorlopen en afgedrukt die deelbaar zijn door 3. Het deconstrueert het tupleresultaat van Int32.DivRem en komt overeen met een Remainder van 0:

for (int i = 4; i < 20;  i++)
{
    if (Math.DivRem(i, 3) is ( Quotient: var q, Remainder: 0 ))
    {
        Console.WriteLine($"{i} is divisible by 3, with quotient {q}");
    }
}

Zie Deconstructing tuples en andere typen voor meer informatie over de deconstructie van tuples en andere typen.

Tuple-gelijkheid

Tuple-typen ondersteunen de == en != operators. Deze operators vergelijken leden van de linkeroperand met de bijbehorende leden van de rechteroperand volgens de volgorde van tuple-elementen.

(int a, byte b) left = (5, 10);
(long a, int b) right = (5, 10);
Console.WriteLine(left == right);  // output: True
Console.WriteLine(left != right);  // output: False

var t1 = (A: 5, B: 10);
var t2 = (B: 5, A: 10);
Console.WriteLine(t1 == t2);  // output: True
Console.WriteLine(t1 != t2);  // output: False

Zoals in het voorgaande voorbeeld wordt weergegeven, houden de == en != bewerkingen geen rekening met tuple-veldnamen.

Twee tuples zijn vergelijkbaar wanneer aan beide van de volgende voorwaarden wordt voldaan:

  • Beide tuples hebben hetzelfde aantal elementen. Hiermee wordt bijvoorbeeld t1 != t2 niet gecompileerd als t1 en t2 verschillende getallen elementen bevatten.
  • Voor elke tuplepositie zijn de bijbehorende elementen van de linker- en rechter tupleoperanden vergelijkbaar met behulp van de == en != operatoren. Compileert bijvoorbeeld (1, (2, 3)) == ((1, 2), 3) niet omdat 1 dit niet vergelijkbaar is met (1, 2).

De == operatoren != vergelijken tuples op een kortsluitingswijze. Dat wil gezegd, een bewerking stopt zodra deze aan een paar niet-gelijke elementen voldoet of de uiteinden van tuples bereikt. Voordat er echter een vergelijking wordt uitgevoerd, worden alle tuple-elementen geëvalueerd, zoals in het volgende voorbeeld wordt weergegeven:

Console.WriteLine((Display(1), Display(2)) == (Display(3), Display(4)));

int Display(int s)
{
    Console.WriteLine(s);
    return s;
}
// Output:
// 1
// 2
// 3
// 4
// False

Tuples als outparameters

Normaal gesproken herstructureert u een methode met out parameters in een methode die een tuple retourneert. Er bestaan echter enkele gevallen waarin een out parameter een tuple-type kan zijn. In het volgende voorbeeld ziet u hoe u met tuples kunt werken als out parameters:

var limitsLookup = new Dictionary<int, (int Min, int Max)>()
{
    [2] = (4, 10),
    [4] = (10, 20),
    [6] = (0, 23)
};

if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits))
{
    Console.WriteLine($"Found limits: min is {limits.Min}, max is {limits.Max}");
}
// Output:
// Found limits: min is 10, max is 20

Tuples versus System.Tuple

C#-tuples gebruiken System.ValueTuple typen en verschillen van tuples die gebruikmaken van System.Tuple typen. De belangrijkste verschillen zijn:

  • System.ValueTuple typen zijn waardetypen. System.Tuple typen zijn referentietypen.
  • System.ValueTuple typen zijn veranderlijk. System.Tuple typen zijn onveranderbaar.
  • Gegevensleden van System.ValueTuple typen zijn velden. Gegevensleden van System.Tuple typen zijn eigenschappen.

C#-taalspecificatie

Zie voor meer informatie:

Zie ook