C# ValueTuple 原理


本文告訴大家一些 ValueTuple 的原理,避免在使用出現和期望不相同的值。ValueTuple 是 C# 7 的語法糖,如果使用的 .net Framework 是 4.7 以前,那么需要使用 Nuget 安裝System.ValueTuple

雖然 ValueTuple 的很好用,但是需要知道他有兩個地方都是在用的時候需要知道他原理。如果不知道原理,可能就發現代碼和預期不相同

json 轉換

先創建一個項目,然后安裝 Json 解析,使用下面的代碼,在運行之前,先猜一下,下面的代碼會出現什么

            var foo = (name: "lindexi", site: "blog.csdn.net/lindexi_gd");
            var str = JsonConvert.SerializeObject(foo);

實際上輸出的是 {"Item1":"lindexi","Item2":"blog.csdn.net/lindexi_gd"}

那么剛才的命名在哪?

如果想知道,那么請看 ValueTuple 的原理

原理

先來寫一段代碼,編譯之后對他反編譯,看一下他是怎么做的

        static void Main(string[] args)
        {
            var foo = Foo();
            var str = JsonConvert.SerializeObject(foo);
            Console.WriteLine(str);
        }

        static (string name, string site) Foo()
        {
            return (name: "lindexi", site: "blog.csdn.net/lindexi_gd");
        }

不需要安裝反編譯軟件,可以使用這個網站拿到反編譯

可以看到Foo被編譯為 TupleElementNames 特性的兩個字符串

    [return: TupleElementNames(new string[]
    {
        "name",
        "site"
    })]
    private static ValueTuple<string, string> Foo()
    {
        return new ValueTuple<string, string>("lindexi", "blog.csdn.net/lindexi_gd");
    }

所以實際上代碼是 ValueTuple<string, string> 不是剛才定義的代碼,只是通過 TupleElementNames 讓編譯器知道值,所以是語法糖。

IL 代碼是

private hidebysig static valuetype [mscorlib]System.ValueTuple`2<string, string> 
    Foo() cil managed 
  {
    .param [0] 
    .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) 
      = (
        01 00 02 00 00 00 04 6e 61 6d 65 04 73 69 74 65 // .......name.site 這里就是 return: TupleElementNames 的命名
        00 00                                           // ..
      )
    .maxstack 2
    .locals init (
      [0] valuetype [mscorlib]System.ValueTuple`2<string, string> V_0
    )

    // [20 9 - 20 10]
    IL_0000: nop          

    // [21 13 - 21 72]
    IL_0001: ldstr        "lindexi"
    IL_0006: ldstr        "blog.csdn.net/lindexi_gd"
    IL_000b: newobj       instance void valuetype [mscorlib]System.ValueTuple`2<string, string>::.ctor(!0/*string*/, !1/*string*/)
    IL_0010: stloc.0      // V_0
    IL_0011: br.s         IL_0013

    // [22 9 - 22 10]
    IL_0013: ldloc.0      // V_0
    IL_0014: ret          

  }

這個特性只有編譯器可以用,不可以在代碼使用。

在上面的解釋,實際上 IL 不知道存在定義的命名,所以不可以通過這個方法獲得值。

動態類型獲得值

如果希望使用動態類型獲得值,那么下面的代碼實際上會運行出現異常

        static void Main(string[] args)
        {
            dynamic foo = Foo();
            Console.WriteLine(foo.name);
        }

        static (string name, string site) Foo()
        {
            return (name: "lindexi", site: "blog.csdn.net/lindexi_gd");
        }

運行出現 RuntimeBinderException 異常,因為沒有發現 name 屬性

實際上對比下面匿名類,也就是很差不多寫法。

        dynamic foo = new { name = "lindexi", site = "blog.csdn.net/lindexi_gd" };
            Console.WriteLine(foo.name);

運行是可以的,所以在使用動態類型,請不要使用 ValueTuple ,如果需要使用,那么請知道有存在找不到變量異常,而且是在運行才出現異常。

其他需要知道的

不要隨便定義一個看不懂的值

實際上下面的代碼,編譯是可以通過

(int x, (int y, (float a, float b))[] c) f1

但是這個值,在看的時候,幾乎說不出他的屬性

第二個需要知道的,ValueTuple 是值類型,所以他的默認值不是 null 而是 default(xx),在C# 7.2 支持使用關鍵字,所以不需要去寫 defalut(xx,xx)

關於 ValueTuple 變量名的定義也是很難說的,有的小伙伴覺得需要使用 Axx 的方式命名,但是很多小伙伴覺得使用 aaBa 的命名更好,所以暫時對於他的命名,大家覺得什么方式好請告訴我

參見: Exploring Tuples as a Library Author

C# 7: Dynamic types and Reflection cannot access Tuple fields by name

我搭建了自己的博客 https://lindexi.gitee.io/ 歡迎大家訪問,里面有很多新的博客。只有在我看到博客寫成熟之后才會放在csdn或博客園,但是一旦發布了就不再更新

如果在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

知識共享許可協議
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系


免責聲明!

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



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