• [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

  • 相关阅读:
    未解之谜!代码中的超自然现象
    「IOI2017」西默夫 / Simurgh
    C++ primer读书笔记
    Vue仿Pc微信客户端
    @datetimeformat 与@JsonFormat的区别使用
    java 判断当前时间在时间范围内方法
    MySQL 索引优化全攻略
    王文 《深水炸弹》(五)关于我纯文学启蒙的一篇文章
    王文 《深水炸弹》(六)关于我纯文学启蒙的一篇文章
    王文 《深水炸弹》(三)关于我纯文学启蒙的一篇文章
  • 原文地址:https://www.cnblogs.com/linianhui/p/csharp7_tuple.html
Copyright © 2020-2023  润新知