C#中ValueTuple的原理詳解
前言
本文告訴大家一些 ValueTuple 的原理,避免在使用出現(xiàn)和期望不相同的值。ValueTuple 是 C# 7 的語(yǔ)法糖,如果使用的 .net Framework 是 4.7 以前,那么需要使用 Nuget 安裝System.ValueTuple
雖然 ValueTuple 的很好用,但是需要知道他有兩個(gè)地方都是在用的時(shí)候需要知道他原理。如果不知道原理,可能就發(fā)現(xiàn)代碼和預(yù)期不相同
json 轉(zhuǎn)換
先創(chuàng)建一個(gè)項(xiàng)目,然后安裝 Json 解析,使用下面的代碼,在運(yùn)行之前,先猜一下,下面的代碼會(huì)出現(xiàn)什么
var foo = (name: "lindexi", site: "blog.csdn.net/lindexi_gd"); var str = JsonConvert.SerializeObject(foo);
實(shí)際上輸出的是 {"Item1":"lindexi","Item2":"blog.csdn.net/lindexi_gd"}
那么剛才的命名在哪?
如果想知道,那么請(qǐng)看 ValueTuple 的原理
原理
先來(lái)寫一段代碼,編譯之后對(duì)他反編譯,看一下他是怎么做的
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");
}
不需要安裝反編譯軟件,可以使用這個(gè)網(wǎng)站拿到反編譯
可以看到Foo被編譯為 TupleElementNames 特性的兩個(gè)字符串
[return: TupleElementNames(new string[]
{
"name",
"site"
})]
private static ValueTuple<string, string> Foo()
{
return new ValueTuple<string, string>("lindexi", "blog.csdn.net/lindexi_gd");
}
所以實(shí)際上代碼是 ValueTuple<string, string> 不是剛才定義的代碼,只是通過(guò) TupleElementNames 讓編譯器知道值,所以是語(yǔ)法糖。
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
}
這個(gè)特性只有編譯器可以用,不可以在代碼使用。
在上面的解釋,實(shí)際上 IL 不知道存在定義的命名,所以不可以通過(guò)這個(gè)方法獲得值。
動(dòng)態(tài)類型獲得值
如果希望使用動(dòng)態(tài)類型獲得值,那么下面的代碼實(shí)際上會(huì)運(yùn)行出現(xiàn)異常
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");
}
運(yùn)行出現(xiàn) RuntimeBinderException 異常,因?yàn)闆](méi)有發(fā)現(xiàn) name 屬性
實(shí)際上對(duì)比下面匿名類,也就是很差不多寫法。
dynamic foo = new { name = "lindexi", site = "blog.csdn.net/lindexi_gd" };
Console.WriteLine(foo.name);
運(yùn)行是可以的,所以在使用動(dòng)態(tài)類型,請(qǐng)不要使用 ValueTuple ,如果需要使用,那么請(qǐng)知道有存在找不到變量異常,而且是在運(yùn)行才出現(xiàn)異常。
性能提升
如果使用 ValueTuple 編程會(huì)有一些優(yōu)點(diǎn),性能是其中之一。而且對(duì)于異步編程,使用 ValueTuple 可以繼續(xù)使用 await 的方法。
假如有一個(gè)方法需要返回 5 個(gè)參數(shù),那么以前的做法有三個(gè)方法,第一個(gè)方法是使用 out 的方法,第二個(gè)方法是使用 Tuple ,第三個(gè)方法是定義一個(gè)臨時(shí)的類。
如果使用了 out 的方法,那么這個(gè)方法就不可以繼續(xù)使用異步 await 的方法,因?yàn)?await 需要做出狀態(tài)機(jī),參見(jiàn)我寫的await原理。如果使用 Tuple ,或這定義一個(gè)臨時(shí)的類,就會(huì)出現(xiàn)性能的問(wèn)題。
從上面的原理,已經(jīng)告訴大家,ValueTuple 是值類型,而 Tuple 或定義的一個(gè)類不是值類型。編譯器的優(yōu)化是讓 ValueTuple 分配在棧,對(duì)于普通的類分配在堆空間。如果一個(gè)類分配到堆空間,那么就需要使用垃圾回收才可以清理空間。而分配到棧就不需要使用垃圾回收,使用完成就清空棧,效率比堆空間大。
但是使用棧空間需要注意,棧空間是很小的,如果使用了大量棧空間可能會(huì)出現(xiàn)堆棧gg。因?yàn)榭紤]到部分剛?cè)腴T的小伙伴,所以我就需要多說(shuō)一些,上面說(shuō)的 ValueTuple 使用了棧空間需要小心??臻g不足,和你存放的值的關(guān)系不大,而是和定義的 ValueTuple 數(shù)量有關(guān),這個(gè)數(shù)量是非常大的。但是在遞歸方法中,本來(lái)是剛好空間足夠的,在使用了 ValueTuple 可能就不夠了。
使用 ValueTuple 可以繼續(xù)使用異步,而且不需要垃圾回收,性能比Tuple高,所以建議在多返回參數(shù)使用 ValueTuple,而不是定義一個(gè)類。
其他需要知道的
不要隨便定義一個(gè)看不懂的值
實(shí)際上下面的代碼,編譯是可以通過(guò)
(int x, (int y, (float a, float b))[] c) f1
但是這個(gè)值,在看的時(shí)候,幾乎說(shuō)不出他的屬性
第二個(gè)需要知道的,ValueTuple 是值類型,所以他的默認(rèn)值不是 null 而是 default(xx),在C# 7.2 支持使用關(guān)鍵字,所以不需要去寫 defalut(xx,xx)
關(guān)于 ValueTuple 變量名的定義也是很難說(shuō)的,有的小伙伴覺(jué)得需要使用 Axx 的方式命名,但是很多小伙伴覺(jué)得使用 aaBa 的命名更好,所以暫時(shí)對(duì)于他的命名使用 aaBa 的方法,大家覺(jué)得什么方式好請(qǐng)告訴我
參見(jiàn): Exploring Tuples as a Library Author
C# 7: Dynamic types and Reflection cannot access Tuple fields by name
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)我們的支持。
您可能感興趣的文章
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并打開的方法
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#停止線程的方法
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新聞效果的方法
- 01-10C#通過(guò)重寫Panel改變邊框顏色與寬度的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實(shí)現(xiàn)多線程下載文件的方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什


