元祖 Tuple 其实就微软内置的一组包含若干个属性的泛型类型,包括结构体类型的 System.ValueTuple、引用类型的 System.Tuple,包含1到8个只读属性。
- System.ValueTuple,是值类型,结构体,成员是字段,可修改。
- System.Tuple 类型是引用类型,成员是只读属性。
📢 优先推荐使用 ValueTuple,这也是微软深度支持的,性能更好,默认类型推断用的都是ValueTuple。Tuple 作为历史的产物,在语言级别没有任何特殊支持。
下面代码为Tuple<T1>
的源代码,就是这么朴实无华,其他就是相等比较、ToString
、索引器。
public struct ValueTuple<T1, T2> { public T1 Item1; public T2 Item2; public ValueTuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; } }
🚩C#在语法层面对
ValueTuple
的操作提供了很多便捷支持,让元祖的使用非常简单、优雅,基本可以替代匿名类型。
- 简化
Tuplec
申明:用括号的简化语法,(Type,Type,...)
,(string,int)
等效于ValueTuple<string,int>
,编译器会进行类型推断。 - 值相等:元祖内部实现了相等比较操作符重载,比较的是字段值。
- 元素命名:元祖可以显示指定字段名称,比原来的无意义Item1、Item2好用多了。不过命名是开发态支持,编译后还是Item1、Item2,因此在运行时(反射)不可用。
- 解构赋值,元祖对解构的支持是编译器行为。
ValueTuple<double,double> p1 = new (1,5); //简化语法 (double, double) p2 = (3, 5.5); var p3 = (3, 5.5); //类型推断,进一步简化 var dis = p2.Item1 * p2.Item2; //Item1、Item2 成员 //值比较 Console.WriteLine(p2 == p3); //True //命名,有名字的元祖 var p4 = (Name:"sam",Age:22); Console.WriteLine(p4.Name); //sam //解构赋值 var (n,age) = p4; Console.WriteLine(n); //sam
元祖的一个比较适用场景就是方法返回多个值,虽然本质上还是一个“值”。
void Main() { var u = FindUser(1); var (nn,ss) = FindUser(2); Console.WriteLine(u.name+u.score); Console.WriteLine(nn+ss); } public (string name,int score) FindUser(int id) //返回一个元祖 { return ("sam",1000); }