C# Tuple 介紹

平時我們臨時要將多個參數傳遞要怎麼做?建立物件?或是使用多個 out? dynamic? 還是 List<object>? 或許有個更好的方法:

Tuple

System.Tuple 翻譯為「元組」,是一種資料結構,用來儲存多種相同或不同類型資料的清單,主要用於暫時性的參數傳遞,直接看範例:
    
// 基本寫法
Tuple<string, int, double> t1 = new Tuple<string, int, double>("Hello", 1, 1.1);
Console.WriteLine($"item1: {t1.Item1}, item2: {t1.Item2}, item3: {t1.Item3}");

// 簡化
var t2 = Tuple.Create<string, int, double>("Hello", 1, 1.1);
Console.WriteLine($"item1: {t2.Item1}, item2: {t2.Item2}, item3: {t2.Item3}");

// 最簡化
var t3 = Tuple.Create("Hello", 1, 1.1);
Console.WriteLine($"item1: {t3.Item1}, item2: {t3.Item2}, item3: {t3.Item3}");
    

Tuple 有長度限制,一個 Tuple 最多只可以儲存 7 個元素和一個 Tuple,假設我就是要放超過 7 個怎麼辦?

Tuple 放完 7 個元素後把剩下的放在新的 Tuple 中,然後這個新的 Tuple 裡面可以再存放 7 個元素和一個 Tuple,然後這個新新的 Tuple 裡面可以再再存放 7 個元素和一個 Tuple...

放 10 個元素的範例:
    
Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>> tuple =
    new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7,
        new Tuple<int, int, int>(8, 9, 10));
        
Console.WriteLine($"item1: {tuple.Item1}, item2: {tuple.Item2}, item3: {tuple.Item3}, item4: {tuple.Item4}, item5: {tuple.Item5}, item6: {tuple.Item6}, item7: {tuple.Item7}, item8: {tuple.Rest.Item1}, item9: {tuple.Rest.Item2}, item10: {tuple.Rest.Item3}");
    

好啦其實可以簡化:
    
var tuple = Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10));
        
Console.WriteLine($"item1: {tuple.Item1}, item2: {tuple.Item2}, item3: {tuple.Item3}, item4: {tuple.Item4}, item5: {tuple.Item5}, item6: {tuple.Item6}, item7: {tuple.Item7}, item8: {tuple.Rest.Item1}, item9: {tuple.Rest.Item2}, item10: {tuple.Rest.Item3}");
    

不過最不方便的是 Tuple 要取用的時候只能以名稱 Item1, Item2 這樣取用,需要記得或是每次比對第幾個是放什麼,非常不直覺,很容易忘記哪個位置放了什麼,尤其是各種類型混在一起的時候。

ValueTuple

於是後來就有了 ValueTuple (在 .NET Framework4.7 以前需要手動安裝 System.ValueTuple),ValueTuple 可以容納更多內容,使用起來更方便,更適合用來傳遞多個參數
    
var t1 = ValueTuple.Create("Hello", 1, 1.1);
Console.WriteLine($"item1: {t1.Item1}, item2: {t1.Item2}, item3: {t1.Item3}");

var t2 = ("Hello", 1, 1.1);
Console.WriteLine($"item1: {t2.Item1}, item2: {t2.Item2}, item3: {t2.Item3}");

// 指定屬性名稱
var t3 = (id: "Hello", name: 1, age: 1.1);
Console.WriteLine($"item1: {t3.id}, item2: {t3.name}, item3: {t3.age}");

// 宣告時指定屬性名稱
(string id, int name, double age) t4 = ("Hello", 1, 1.1);

// 可以直接使用 id, name, age
var (id, name, age) = ("Hello", 1, 1.1);
Console.WriteLine($"item1: {id}, item2: {name}, item3: {age}");
    

話說 ValueTuple 這麼厲害還要用匿名類別嗎?最重要的是 ValueTuple 無法被序列化,比較難直接儲存,其他差異可以見官方的這張表



參考資料:
Microsoft.Learn - Tuple Class
Microsoft.Learn - ValueTuple Struct
Microsoft.Learn - Choosing between anonymous and tuple types

留言