Rekordtípusok (C#-referencia)

A tuples funkció tömör szintaxist biztosít több adatelem csoportosításához egy egyszerűsített adatstruktúrában. Az alábbi példa bemutatja, hogyan deklarálhat egy rekordváltozót, inicializálhatja és érheti el az adattagokat:

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

Ahogy az előző példa is mutatja, a rekordtípus meghatározásához meg kell adnia az összes adattag típusát, és opcionálisan a mezőneveket is. Nem definiálhat metódusokat rekordtípusban, de a .NET által biztosított metódusokat használhatja, ahogy az alábbi példa mutatja:

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

A tuple típusok támogatják az egyenlőségi operátorokat== és !=a . További információ: Tuple egyenlőség szakasz.

A rekordtípusok értéktípusok, a rekordelemek nyilvános mezők. Ez módosítja a módosítható értéktípusokat.

Tetszőleges számú elemet tartalmazó halmazokat definiálhat:

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

A csuplok használata

A csuplok egyik leggyakoribb használati esete a metódus visszatérési típusa. Vagyis a metódusparaméterek definiálása out helyett a metóduseredményeket egy rekord típusú visszatérési típusba csoportosíthatja, ahogy az alábbi példa is mutatja:

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

Ahogy az előző példa is mutatja, a visszaadott rekordpéldányt közvetlenül is használhatja, vagy külön változókban dekonstruálhatja .

Névtelen típusok helyett használhat rekordtípusokat is, például LINQ-lekérdezésekben. További információ: A névtelen és a rekordtípus közötti választás.

Általában a kötetlenül kapcsolódó adatelemek csoportosításához használunk uplest. A nyilvános API-kban érdemes lehet osztályt vagy struktúratípust definiálni.

Rekordmezők nevei

A rekordmezők nevét explicit módon megadhatja egy tuple inicializálási kifejezésben vagy egy rekordtípus definíciójában, ahogyan az alábbi példa mutatja:

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

Ha nem ad meg mezőnevet, az egy tuple inicializálási kifejezés megfelelő változójának nevéből következtethet, ahogy az alábbi példa is mutatja:

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

Ezt nevezik tuple-vetületi inicializálóknak. A változó neve nem lesz előrevetítve egy rekordmező nevére a következő esetekben:

  • A jelölt neve egy tuple típusú tagnév, Item3például , ToStringvagy Rest.
  • A jelölt neve egy másik, explicit vagy implicit név duplikáltja.

Az előző esetekben vagy explicit módon adja meg egy mező nevét, vagy az alapértelmezett nevével fér hozzá egy mezőhöz.

A rekordmezők alapértelmezett nevei a következőkItem1Item2Item3, és így tovább. Egy mező alapértelmezett nevét mindig használhatja, még akkor is, ha egy mezőnév explicit módon van megadva vagy következtetve, ahogy az alábbi példa mutatja:

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.

A rekord-hozzárendelés és a rekordegyenlőség-összehasonlítás nem veszi figyelembe a mezőneveket.

Fordításkor a fordító lecseréli a nem alapértelmezett mezőneveket a megfelelő alapértelmezett nevekre. Ennek eredményeképpen a explicit módon megadott vagy a kikövetkeztetett mezőnevek nem érhetők el futásidőben.

Tipp.

Engedélyezze a .NET-kódstílus-szabály IDE0037 beállítását a kikövetkzett vagy explicit rekordmezőnevekre.

A C# 12-től kezdődően megadhat egy aliast egy rekordtípushoz egy using direktívával. Az alábbi példa egy global using rekordtípus aliasát adja hozzá két egész számmal egy engedélyezett Min és Max egy értékhez:

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

Az alias deklarálása után a BandPass nevet használhatja aliasként az adott típushoz:

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

Az aliasok nem vezetnek be új típust, csak szinonimát hoznak létre egy meglévő típushoz. Az aliassal BandPass deklarált rekordokat ugyanúgy dekonstruálhatja, mint a mögöttes rekordtípussal:

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

A tuple-hozzárendeléshez vagy -dekonstruáláshoz hasonlóan a rekordtagok neveinek nem kell egyeznie; a típusok igen.

Hasonlóképpen, az azonos aritású és tagtípusú második alias felcserélhető az eredeti aliassal. Deklarálhat egy második aliast:

using Range = (int Minimum, int Maximum);

A rekordokat Range hozzárendelheti egy BandPass tálhoz. Mint minden rekord-hozzárendelés esetében, a mezőneveknek nem kell egyeznie, csak a típusoknak és az aritásnak.

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

A rekordtípus aliasa több szemantikai információt biztosít a rekordok használatakor. Nem vezet be új típust. A típusbiztonság biztosításához inkább egy pozíciót record kell deklarálnia.

Tuple-hozzárendelés és dekonstruálás

A C# támogatja a két típus közötti hozzárendelést, amely megfelel az alábbi feltételeknek:

  • mindkét típus azonos számú elemből áll
  • az egyes ugrópozíciók esetében a jobb oldali ugróelem típusa megegyezik vagy implicit módon átalakítható a megfelelő bal oldali ugróelem típusával

A rekordelemek értékei a rekordelemek sorrendjét követve vannak hozzárendelve. A rekordmezők neve figyelmen kívül lesz hagyva, és nincs hozzárendelve, ahogy az alábbi példa mutatja:

(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

A hozzárendelési operátorral =külön változókban bonthat fel egy rekordpéldányt. Ezt többféleképpen is megteheti:

  • var A zárójelen kívüli kulcsszóval deklarálhatja az implicit módon beírt változókat, és hagyhatja, hogy a fordító a típusukra következtet:

    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.
    
  • Explicit módon deklarálja az egyes változók típusát zárójelben:

    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.
    
  • Egyes típusokat explicit módon és más típusokat implicit módon (a varzárójelek között) deklarál:

    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.
    
  • Meglévő változók használata:

    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.
    

A dekonstruálási kifejezés célhelye lehet a dekonstruálási deklarációban deklarált meglévő változók és változók is.

A dekonstruálást mintaegyeztetéssel is kombinálhatja a mezők jellemzőinek vizsgálatához. Az alábbi példa több egész számon végighalad, és a 3-tal osztható számokat nyomtatja ki. A 0-ra bontja a rekorderedményt Int32.DivRem és a találatokat Remainder :

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

A csonkok és más típusok dekonstruálásával kapcsolatos további információkért lásd : Deconstructing tuples and other types.

Rekordegyenlőség

A rekordtípusok támogatják az és != az == operátorokat. Ezek az operátorok összehasonlítják a bal oldali operandus tagjait a jobb oldali operandus megfelelő tagjaival a ugróelemek sorrendjét követve.

(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

Ahogy az előző példa is mutatja, az és != a == műveletek nem veszik figyelembe a rekordmezőneveket.

Két csupl hasonlítható össze, ha az alábbi feltételek teljesülnek:

  • Mindkét csupl ugyanolyan számú elemből áll. Például nem fordítja le, t1 != t2 ha t1 és t2 különböző számú elemből áll.
  • A bal és a jobb oldali operandusok megfelelő elemei minden egyes ugrópozícióhoz összehasonlíthatók az operátorokkal és != az == operátorokkal. Például nem fordítható le, (1, (2, 3)) == ((1, 2), 3) mert 1 nem hasonlítható össze.(1, 2)

Az == operátorok != rövidzárolással hasonlítják össze a kapcsolásokat. Vagyis egy művelet leáll, amint megfelel egy nem egyenlő elempárnak, vagy eléri a csuplok végét. Az összehasonlítás előtt azonban az összes rekordelem kiértékelése történik, ahogy az alábbi példa is mutatja:

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

Kiugró paramétereket ad meg

Általában egy olyan metódus újrabontása, amely paraméterekkel rendelkezik out egy olyan metódusban, amely egy rekordot ad vissza. Vannak azonban olyan esetek, amikor egy out paraméter rekordtípusú lehet. Az alábbi példa azt mutatja be, hogyan használható a csuplok paraméterként out :

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

Csuples vs System.Tuple

A típusok által System.ValueTuple támogatott C#-csuplok eltérnek a típusok által System.Tuple képviselt csuploktól. A fő különbségek a következők:

  • System.ValueTuple a típusok értéktípusok. System.TupleA típusok referenciatípusok.
  • System.ValueTuple típusok nem módosíthatók. System.Tuple típusok nem módosíthatók.
  • A típusok adattagjainak mezői System.ValueTuple . A típusok adattagja System.Tuple tulajdonságok.

C# nyelvspecifikáció

További információkért lásd:

Lásd még