Menu

C# Tuple: Adlandırılmış Tuple'lar, Birden Fazla Değer Döndürmek, Deconstruction

Bir C# tuple'ı, bir tip bildirmeden birkaç değeri tek bir değerde toplar. Value tuple sözdizimini, adlandırılmış elemanları, bir metottan birden fazla değer döndürmeyi, var (a, b) ile ayrıştırmayı, bir dictionary'de tuple anahtarlarını ve ValueTuple'ın eski System.Tuple'dan farkını öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Bir tuple, onun için bir sınıf bildirmeden küçük ve sabit sayıda değeri tek bir değerde toplar. ("Ana", 31) bir string ve bir int'ten oluşan bir tuple'dır. Tuple'lar bir metottan iki üç şey döndürmenin ve bir metot içinde kısa ömürlü çiftleri tutmanın deyimsel yoludur.

Bir tuple oluşturmak

Tip de parantezlerle yazılır: (string, int), ilk elemanı bir string, ikincisi bir int olan bir tuple tipidir.

Çıktı:

Ana is 31
(3, 4)
179.70
(Ana, 32)

Adlar olmadan elemanlar Item1, Item2, Item3 vb. olur. Bu tuple'lar System.ValueTuple struct'larıdır, bu yüzden birini başka bir değişkene atamak değerleri kopyalar ve kopyayı değiştirmek orijinale dokunmaz.

Adlandırılmış elemanlar

Item1 değerin ne olduğu hakkında hiçbir şey söylemez. Elemanlara tipte ya da literal'de ad verin:

Çıktı:

Ana, 31
Desk lamp costs 24.99
Ana
Ana

Adlar derleme zamanı kolaylığıdır. Çalışma zamanında yukarıdaki iki tuple da düz ValueTuple<string, int> değerleridir; person'ı farklı adlara sahip bir tuple'a atamanın derlenmesinin nedeni budur: yalnızca eleman tipleri ve sıraları eşleşmelidir.

C# 7.1 ve sonrası adları literal'deki değişkenlerden de çıkarır, bu yüzden var t = (name, age); onları iki kez yazmadan size t.name ve t.age verir. C# 7.0'da bu elemanlar yalnızca Item1 ve Item2'dir.

Bir metottan birden fazla değer döndürmek

Tuple'ların dile eklenmesinin asıl nedeni budur. Onlardan önce seçenekler out parametreleri, tek kullanımlık bir sınıf ya da anonim Item1'iyle System.Tuple'dı.

Çıktı:

Lowest 60, highest 95
Average 78.75

Dönüş tipindeki eleman adları çağıranın gördüğü adlar olur. Aynı metodu out parametreleriyle karşılaştırın:

static void Stats(int[] scores, out int min, out int max, out double average) { ... }

Stats(scores, out int min, out int max, out double avg);

out parametreleri, bir bool'un işe yarayıp yaramadığını söylediği ve değerin onun yanında geri geldiği TryParse şekli için gelenek olmaya devam eder. "Birkaç şey hesapla ve döndür" için bir tuple daha iyi okunur. Bkz. ref ve out.

Deconstruction

Deconstruction bir tuple'ı tek bir ifadede ayrı değişkenlere açar:

Çıktı:

Lima 24.5
Lima
24.5
2 1

var (city, temp)'teki değişken adları sizindir; tuple'ın eleman adlarıyla eşleşmeleri gerekmez, çünkü deconstruction konuma göre çalışır. (a, b) = (b, a) takası önce eski değerlerden bir tuple oluşturur, sonra atar, bu yüzden hiçbir değer kaybolmaz.

Kendi sınıflarınız, out parametreli bir Deconstruct metodu bildirerek deconstruction'ı destekleyebilir. C# 7 ile .NET Core 2.0 ya da sonrasında KeyValuePair'in de bir tane vardır, bu yüzden bir dictionary döngüsü her girdiyi başlığında açabilir:

class Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) { X = x; Y = y; }
    public void Deconstruct(out int x, out int y) { x = X; y = Y; }
}

var (x, y) = new Point(3, 4);

foreach (var (name, score) in scoresByName)   // Dictionary<string, int>
{
    Console.WriteLine($"{name}: {score}");
}

Aynı foreach (var (a, b) in listOfTuples) biçimi bir tuple listesi için de çalışır. Sonraki bölümün yaptığı gibi tüm tuple'ı tek bir döngü değişkeni olarak alıp adlandırılmış elemanlarını okumak da aynı derecede iyi çalışır.

Listelerde, LINQ'te ve dictionary'lerde tuple'lar

Adlandırılmış tuple'lardan oluşan bir liste, bir metot içinde veri satırlarını tutmanın hafif bir yoludur:

Çıktı:

Chloe 95
Ana 88
Ben 72
(Ana, B), (Chloe, A)
Ben
False

ValueTuple, Equals ve GetHashCode'u eleman eleman uygular, bu yüzden iki farklı yerde oluşturulan (2, 7) aynı dictionary girdisini bulur. Bu, tuple'ı en basit bileşik anahtar yapar: sınıf yok, elle yazılmış hash fonksiyonu yok, row + ":" + col gibi string birleştirme yok.

Tuple eşitliği

Equals eleman eleman karşılaştırır ve tuple'lar tanıtıldığından beri çalışır:

Çıktı:

True
False

C# 7.3, tuple'lar için aynı eleman eleman karşılaştırmaya derlenen ve adları da yok sayan == ve !='i ekledi:

if ((order.Status, order.Paid) == ("shipped", true)) { ... }
Console.WriteLine((1, "x") == (1, "x"));   // True

System.Tuple ile ValueTuple

.NET 4, Tuple.Create ile oluşturulan bir sınıf olan System.Tuple'ı getirdi. Hâlâ framework'te bulunur ve eski kodda ve API'lerde hâlâ karşınıza çıkar; "C# tuple" aramalarının sık sık ona çıkmasının nedeni budur.

ValueTuple (C# 7)System.Tuple (.NET 4)
Sözdizimi(1, "a"), (int, string)Tuple.Create(1, "a"), Tuple<int, string>
Türüstruct (değer tipi)class (referans tipi)
Eleman adlarıEvetHayır, yalnızca Item1, Item2
DeğiştirilebilirEvet, elemanlar alandırHayır, salt okunur property'ler
DeconstructionEvetEvet, extension metotlar aracılığıyla

Çıktı:

Ben 25
(Ben, 25)

ToValueTuple() ve ToTuple() ikisi arasında dönüştürür. Yeni kodda value tuple'ları tercih edin: heap'te hiçbir şey ayırmazlar ve ad taşıyabilirler.

Bunun yerine ne zaman sınıf ya da record kullanılmalı

Tuple'lar, gruplama yerel ve açık olduğunda en iyisidir: bir metot iki sayı döndürür, bir LINQ sorgusu bir çifti sonraki adıma taşır, bir dictionary iki parçalı bir anahtara ihtiyaç duyar. Şu durumlarda adlandırılmış bir tipe geçin:

  • Aynı şekil birkaç public metot imzasında görünüyorsa. Beş yerde (string, string, int), henüz yazılmamış bir sınıftır.
  • Değerler davranışa, doğrulamaya ya da üç dörtten fazla elemana ihtiyaç duyuyorsa.
  • Veri serileştirme gibi bir sınırı geçiyorsa. Adlar çalışma zamanında yoktur, bu yüzden Newtonsoft.Json Item1 ve Item2 yazar, System.Text.Json ise {} yazar, çünkü tuple elemanları alandır ve varsayılan olarak onları atlar.

C# 9 record'ları, gerçek bir adla birlikte bir tuple'ın kolaylığının çoğunu verir. record Score(string Name, int Points); yerleşik değer eşitliğine, okunabilir bir ToString'e ve deconstruction'a sahiptir. Bkz. record'lar.

Yaygın hatalar

  • Adların çalışma zamanında hayatta kalmasını beklemek. Reflection ve serializer'lar Item1, Item2 görür (ya da hiçbir şey görmez).
  • Bir kopyayı değiştirmek. Tuple'lar struct'tır: var t2 = t1; t2.Item1 = 5; t1'i değiştirmez.
  • Çok elemanlı tuple'lar. Üç dördü geçince adlandırılmış bir tip daha iyi okunur.
  • Deconstruction sırasını karıştırmak. Konuma göredir: bir (Name, Age) tuple'ıyla var (age, name) = person; adı age'e koyar ve var tipleri çıkardığı için hiçbir şey sizi uyarmaz.

Sıkça Sorulan Sorular

C#'ta bir metottan birden fazla değer nasıl döndürülür?

Bir tuple döndürün: dönüş tipini (int Min, int Max) olarak bildirin ve return (lowest, highest); yazın. Çağıran result.Min ve result.Max'i okur ya da var (min, max) = MinMax(data); ile ayrıştırır. Alternatifler TryParse kalıbına uyan out parametreleri ya da sonucun kendine ait bir anlamı olduğunda küçük bir sınıf ya da record'dur.

C#'ta adlandırılmış tuple nedir?

Elemanlarının Item1, Item2 yerine adları olan bir tuple'dır: (string Name, int Age) person = ("Ana", 31); person.Name yazmanızı sağlar. Adlar yalnızca derleme zamanında vardır; altta hâlâ bir ValueTuple<string, int>'tir ve Item1 çalışmaya devam eder.

C#'ta Tuple ile ValueTuple arasındaki fark nedir?

System.Tuple (.NET 4'ten) bir sınıftır: heap'te ayrılır, değiştirilemez ve elemanları yalnızca Item1, Item2'dir. System.ValueTuple (C# 7) değiştirilebilir alanlara sahip bir struct'tır, eleman adlarını ve (a, b) sözdizimini destekler ve her tuple literal'inin oluşturduğu şeydir. Yeni kodda value tuple kullanın.

C#'ta tuple deconstruction nasıl çalışır?

var (name, age) = person; iki değişken bildirir ve tuple'ın elemanlarını sırayla onlara atar. (a, b) = (b, a) ile mevcut değişkenlere de atayabilirsiniz (bu onları takas eder) ve _ discard'ıyla elemanları atlayabilirsiniz: var (_, age) = person;.

C#'ta tuple'lar == ile karşılaştırılabilir mi?

C# 7.3'ten beri == ve != tuple'ları eleman eleman karşılaştırır: (1, "a") == (1, "a") true'dur. Adlar yok sayılır; yalnızca konumlar ve değerler önemlidir. C# 7.3'ten önce value tuple'ların aynı şekilde uyguladığı Equals'ı kullanın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA