[C#7] 1.Tuples(元組)


1. 老版本代碼

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         var fullName = GetFullName();
 6 
 7         Console.WriteLine(fullName.Item1);// Item1,2,3不能忍,,,
 8         Console.WriteLine(fullName.Item2);
 9         Console.WriteLine(fullName.Item3);
10     }
11     static Tuple<string, string, string> GetFullName() => new Tuple<string, string, string>("first name", "blackheart", "last name");
12 }

在有些場景下,我們需要一個方法返回一個以上的返回值,微軟在.NET 4中引入了Tuple這個泛型類,可以允許我們返回多個參數,每個參數按照順序被命名為 Item1;Item2,Item3 ,算是部分的解決了我們的問題,但是對於強迫症程序員來說,Item1,2,3的命名簡直是不能忍的,,,so,在C#7中,引入了一個新的泛型類型ValueTuple<T>來解決這個問題,這個類型位於一個單獨的dll(System.ValueTuple)中,可以通過nuget來引入到你當前的項目中(https://www.nuget.org/packages/System.ValueTuple/)。

2. ValueTuple

不廢話,直接看代碼:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         var fullName = GetFullName();
 6 
 7         Console.WriteLine(fullName.First);  // 終於可以不是Item1,2,3了,,,  8         Console.WriteLine(fullName.Middle);
 9         Console.WriteLine(fullName.Last);
10     }
11 
12     static (string First, string Middle, string Last) GetFullName() => ("first name", "blackheart", "last name");
13 }

看出來差別了嗎?我們終於可以用更直觀的名字來替換掉該死的"Item1,2,3"了,看起來很棒吧。但是貌似我們並沒有用到上面我提到的System.ValueTuple,我們翻開編譯后的程序集看看:

 1 internal class Program
 2 {
 3     private static void Main(string[] args)
 4     {
 5         ValueTuple<string, string, string> fullName = Program.GetFullName();
 6         Console.WriteLine(fullName.Item1); // 原來你還是Item1,2,3,,,FUCK!!!  7         Console.WriteLine(fullName.Item2);
 8         Console.WriteLine(fullName.Item3);
 9     }
10 
11     [TupleElementNames(new string[]
12     {
13             "First",
14             "Middle",
15             "Last"
16     })]
17     private static ValueTuple<string, string, string> GetFullName()
18     {
19         return new ValueTuple<string, string, string>("first name", "blackheart", "last name");
20     }
21 }

不看不知道,一看嚇一跳,原來我們的 fullName.First; 編譯后居然還是 fullName.Item1 ,真是日了狗了。。。

不同之處在於GetFullName這個方法,編譯器把我們簡化的語法形式翻譯成了 ValueTuple<string, string, string> ,還給加了一個新的Attribute(TupleElementNamesAttribute),然后把我們自定義的非常直觀友好的“First”,"Middle","Last"當作元數據給存起來了(如果只是局部使用,則不會添加這樣的元數據)。TupleElementNamesAttribute和ValueTuple一樣,位於System.ValueTuple的單獨dll中。

3. Example

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         var range = (first: 1, end: 10);
 6         //也可以這樣寫,效果是一樣的,編譯后都是沒有了first,end的痕跡,,,first和end只是語法層面的障眼法
 7         //(int first, int last) range = (1, 10);
 8         Console.WriteLine(range.first);
 9         Console.WriteLine(range.end);
10 
11         //可以使用var,這種無顯示聲明一個變量的方式會編譯出多余的代碼,慎用,不知是不是還未優化好。
12         (var begin, var end) = (DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
13         Console.WriteLine(begin);
14         Console.WriteLine(end);
15 
16         //begin,end可以被覆蓋重命名為startDate和endDate,但是會有一個編譯警告,提示名字被忽略掉了。
17         //warning CS8123: The tuple element name 'begin' is ignored because a different name is specified by the target type '(DateTime startDate, DateTime endDate)'
18         //warning CS8123: The tuple element name 'end' is ignored because a different name is specified by the target type '(DateTime startDate, DateTime endDate)‘
19         (DateTime startDate, DateTime endDate) timeSpan = (begin: DateTime.Parse("2017-1-1"), end: DateTime.Parse("2017-12-31"));
20         Console.WriteLine(timeSpan.startDate);
21         Console.WriteLine(timeSpan.endDate);
22     }
23 }

look一下編譯后的代碼:

 1 private static void Main(string[] args)
 2 {
 3     ValueTuple<int, int> range = new ValueTuple<int, int>(1, 10);
 4     Console.WriteLine(range.Item1);
 5     Console.WriteLine(range.Item2);
 6     ValueTuple<DateTime, DateTime> expr_3C = new ValueTuple<DateTime, DateTime>(DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
 7     DateTime item = expr_3C.Item1;
 8     DateTime item2 = expr_3C.Item2;
 9     DateTime begin = item;
10     DateTime end = item2;
11     Console.WriteLine(begin);
12     Console.WriteLine(end);
13     ValueTuple<DateTime, DateTime> timeSpan = new ValueTuple<DateTime, DateTime>(DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
14     Console.WriteLine(timeSpan.Item1);
15     Console.WriteLine(timeSpan.Item2);
16 }

注意 (var begin, var end) = (DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31")); 這一行的便宜結果,看起來很是糟糕(上述6-10行紅色部分),可能還是編譯優化不足的問題吧(release編譯也是如此)。

4. 總結

新的語法形式確實直觀友好了好多,but,本質依然是借助泛型類型來實現的,同時也需要編譯器對新語法形式的支持。

了解了本質是什么東西之后,以后在項目中環境允許的話,就放心大膽的使用吧(類型ValueTuple可以出現的地方,(first,last)這種新語法形式均可以)。

參考:

https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

https://docs.microsoft.com/en-us/dotnet/articles/csharp/tuples

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM