Dela via


Tuppelns typer (C#-referens)

Funktionen tupplar ger en koncis syntax för att gruppera flera dataelement i en enkel datastruktur. I följande exempel visas hur du kan deklarera en tupppelvariabel, initiera den och komma åt dess datamedlemmar:

(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.

Som föregående exempel visar, för att definiera en tuppeln typ, anger du typer av alla dess datamedlemmar och, om du vill, fältnamnen. Du kan inte definiera metoder i en tuppelns typ, men du kan använda metoderna som tillhandahålls av .NET, som följande exempel visar:

(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.

Tuppelns typer stöder likhetsoperatorer== och !=. Mer information finns i avsnittet Tuple equality (Tuppelns likhet ).

Tuppelns typer är värdetyper. Tuppelns element är offentliga fält. Det gör tupplar föränderliga värdetyper.

Du kan definiera tupplar med ett godtyckligt stort antal element:

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

Användningsfall för tupplar

Ett av de vanligaste användningsfallen för tupplar är som en metodreturtyp. I stället för att definiera metodparametrar kan du gruppera out metodresultat i en tuppelns returtyp, vilket visas i följande exempel:

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

Som föregående exempel visar kan du arbeta med den returnerade tuppeln direkt eller dekonstruera den i separata variabler.

Du kan också använda tupplar i stället för anonyma typer, till exempel i LINQ-frågor. Mer information finns i Välja mellan anonyma typer och tupppeltyper.

Vanligtvis använder du tupplar för att gruppera löst relaterade dataelement. Överväg att definiera en klass eller en strukturtyp i offentliga API:er.

Namn på tuppelns fält

Du anger uttryckligen namn på tuppelns fält i ett tuppelns initieringsuttryck eller i definitionen av en tuppelns typ, som följande exempel visar:

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

Om du inte anger ett fältnamn kan det härledas från namnet på motsvarande variabel i ett tuppelns initieringsuttryck, som följande exempel visar:

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

Det kallas tuppeln projektion initierare. Namnet på en variabel projiceras inte på ett tuppelns fältnamn i följande fall:

  • Kandidatnamnet är ett medlemsnamn av tuppelns typ, Item3till exempel , ToString, eller Rest.
  • Kandidatnamnet är en dubblett av ett annat tupppelfältnamn, antingen explicit eller implicit.

I föregående fall anger du antingen uttryckligen namnet på ett fält eller får åtkomst till ett fält som standardnamn.

Standardnamnen för tuppelns fält är Item1, Item2Item3 och så vidare. Du kan alltid använda standardnamnet för ett fält, även när ett fältnamn anges uttryckligen eller härleds, som följande exempel visar:

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.

Jämförelse av tuppelns tilldelning och tuppelns likhet tar inte hänsyn till fältnamn.

Vid kompileringstillfället ersätter kompilatorn fältnamn som inte är standard med motsvarande standardnamn. Därför är uttryckligen angivna eller härledda fältnamn inte tillgängliga vid körning.

Dricks

Aktivera .NET-kodformatregel IDE0037 för att ange en inställning för angivna eller explicita tuppelns fältnamn.

Från och med C# 12 kan du ange ett alias för en tuppelns typ med ett using direktiv. I följande exempel läggs ett global using alias till för en tuppelns typ med två heltalsvärden för ett tillåtet Min och Max värde:

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

När du har deklarerat aliaset kan du använda namnet BandPass som ett alias för den tuppeln:

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

Ett alias introducerar inte någon ny typ, utan skapar bara en synonym för en befintlig typ. Du kan dekonstruera en tuppeln som deklarerats med aliaset BandPass på samma sätt som du kan med dess underliggande tuppelns typ:

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

Precis som med tuppelns tilldelning eller dekonstruktion behöver inte tuppelns medlemsnamn matcha. som typerna gör.

På samma sätt kan ett andra alias med samma aritets- och medlemstyper användas omväxlande med det ursprungliga aliaset. Du kan deklarera ett andra alias:

using Range = (int Minimum, int Maximum);

Du kan tilldela en Range tuppeln till en BandPass tuppeln. Som med all tuppelns tilldelning behöver fältnamnen inte matcha, bara typerna och ariteten.

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

Ett alias för en tupplar innehåller mer semantisk information när du använder tupplar. Den introducerar ingen ny typ. För att ge typsäkerhet bör du deklarera en positionell record i stället.

Tuppelns tilldelning och dekonstruktion

C# stöder tilldelning mellan tuppelns typer som uppfyller båda följande villkor:

  • båda tuppeln har samma antal element
  • För varje tuppelns position är typen av tuppelns högra element samma som eller implicit konvertibel till typen av motsvarande vänster tuppelns element

Tuppelns elementvärden tilldelas efter ordningen på tuppelns element. Namnen på tuppelns fält ignoreras och tilldelas inte, som följande exempel visar:

(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

Du kan också använda tilldelningsoperatorn = för att dekonstruera en tupplarinstans i separata variabler. Du kan göra det på många sätt:

  • Använd nyckelordet var utanför parenteserna för att deklarera implicit inskrivna variabler och låta kompilatorn härleda sina typer:

    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.
    
  • Deklarera uttryckligen typen av varje variabel inom parenteser:

    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.
    
  • Deklarera vissa typer explicit och andra typer implicit (med var) inom parenteserna:

    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.
    
  • Använd befintliga variabler:

    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.
    

Målet för ett dekonstruktionsuttryck kan innehålla både befintliga variabler och variabler som deklareras i dekonstruktionsdeklarationen.

Du kan också kombinera dekonstruktion med mönstermatchning för att inspektera egenskaperna för fält i en tuppeln. Följande exempel loopar igenom flera heltal och skriver ut de som är delbara med 3. Det dekonstruerar tuppeln resultatet av Int32.DivRem och matchar mot en Remainder av 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}");
    }
}

Mer information om dekonstruktion av tupplar och andra typer finns i Dekonstruera tupplar och andra typer.

Tuppelns likhet

Tuppelns typer stöder operatorerna == och != . Dessa operatorer jämför medlemmar i den vänstra operanden med motsvarande medlemmar i den högra operanden efter ordningen på tuppelns element.

(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

Som föregående exempel visar == tar åtgärderna och != inte hänsyn till tuppelns fältnamn.

Två tupplar är jämförbara när båda följande villkor är uppfyllda:

  • Båda tupplar har samma antal element. Kompilerar till exempel t1 != t2 inte om t1 och t2 har olika antal element.
  • För varje tupplarposition är motsvarande element från de vänstra och högra tuppelns operander jämförbara med operatorerna == och != . Kompileras till exempel (1, (2, 3)) == ((1, 2), 3) inte eftersom 1 den inte är jämförbar med (1, 2).

Operatorerna == och != jämför tupplar på kortslutningssätt. Det innebär att en åtgärd stoppas så snart den uppfyller ett par icke-lika element eller når tupplars ändar. Innan någon jämförelse utvärderas dock alla tuppelns element, vilket visas i följande exempel:

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

Tupplar som utparametrar

Vanligtvis omstrukturerar du en metod som har out parametrar till en metod som returnerar en tuppeln. Det finns dock fall där en out parameter kan vara av tuppelns typ. I följande exempel visas hur du arbetar med tupplar som out parametrar:

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

Tupplar jämfört med System.Tuple

C#-tupplar, som backas upp av System.ValueTuple typer, skiljer sig från tupplar som representeras av System.Tuple typer. De största skillnaderna är följande:

  • System.ValueTuple typer är värdetyper. System.Tuple typer är referenstyper.
  • System.ValueTuple typerna är föränderliga. System.Tuple typer är oföränderliga.
  • Datamedlemmar av System.ValueTuple typer är fält. Datamedlemmar av System.Tuple typer är egenskaper.

Språkspecifikation för C#

Mer information finns i:

Se även