Tuple-typen (C#-verwijzing)

De tuples-functie biedt beknopte syntaxis voor het groeperen van meerdere gegevenselementen in een lichtgewicht gegevensstructuur. 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, om een tuple-type te definiëren, typen van alle bijbehorende gegevensleden en, optioneel, de veldnamen op. 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 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 van tuples

Een van de meest voorkomende gebruiksscenario's van tuples is als een retourtype methode. In plaats van methodeparameters te definiëren, kunt u de resultaten van de methode groeperen out in een tuple-retourtype, zoals in het volgende voorbeeld wordt weergegeven:

int[] xs = [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 = [-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.

Normaal gesproken gebruikt u 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 de namen van tuple-velden 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 deze worden afgeleid van 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}.");

Dat 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 tuplevelden zijn Item1, Item2Item3 enzovoort. 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

Waarden voor tuple-elementen worden toegewezen volgens de volgorde van tuple-elementen. De namen van tuple-velden worden genegeerd en niet toegewezen, 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

U kunt de toewijzingsoperator = ook gebruiken 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 tupelpositie zijn de bijbehorende elementen van de linker- en rechter tupleoperanden vergelijkbaar met de operatoren en != de == operatoren. Compileert bijvoorbeeld (1, (2, 3)) == ((1, 2), 3) niet omdat 1 dit niet vergelijkbaar is met (1, 2).

De == operatoren != vergelijken tuples op kortesluiting. 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 zijn echter gevallen waarin een out parameter van 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, die worden ondersteund door System.ValueTuple typen, verschillen van tuples die worden vertegenwoordigd door System.Tuple typen. De belangrijkste verschillen zijn als volgt:

  • 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