日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

linq 查询的结果会开辟新的内存吗?

發布時間:2023/12/4 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linq 查询的结果会开辟新的内存吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一:背景

1. 講故事

昨天群里有位朋友問:linq 查詢的結果會開辟新的內存嗎?如果開了,那是對原序列集里面元素的深拷貝還是僅僅拷貝其引用?

其實這個問題我覺得問的挺好,很多初學 C# 的朋友或多或少都有這樣的疑問,甚至有 3,4 年工作經驗的朋友可能都不是很清楚,這就導致在寫代碼的時候總是會畏手畏腳,還會莫名的揪心這樣玩的話內存會不會暴漲暴跌,這一篇我就用 windbg 來幫助朋友徹底分析一下。

二:尋找答案

1. 一個小案例

這位老弟提到了是深拷貝還是淺拷貝,本意就是想問:linq 一個引用類型集合 到底會怎樣? 這里我先模擬一個集合,代碼如下:

class?Program{static?void?Main(string[]?args){var?personList?=?new?List<Person>()?{new?Person()?{?Name="jack",?Age=20?},new?Person()?{?Name="elen",Age=25,??},new?Person()?{??Name="john",?Age=22?}};var?query?=?personList.Where(m?=>?m.Age?>?20).ToList();Console.WriteLine($"query.count={query.Count}");Console.ReadLine();}}class?Person{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}}

2. 真的是深copy嗎?

如果用 windbg 的話,就非常簡單了,假設是深copy 的話,那么 query 之后,托管堆上就會有 5個 Person,那是不是這樣呢?用 !dumpheap -stat -type Person 到托管堆驗證一下即可。

0:000>?!dumpheap?-stat?-type?Person Statistics:MT????Count????TotalSize?Class?Name 00007ff7f27c3528????????1???????????64?System.Func`2[[ConsoleApp5.Person,?ConsoleApp5],[System.Boolean,?System.Private.CoreLib]] 00007ff7f27c2b60????????2???????????64?System.Collections.Generic.List`1[[ConsoleApp5.Person,?ConsoleApp5]] 00007ff7f27c9878????????1???????????72?System.Linq.Enumerable+WhereListIterator`1[[ConsoleApp5.Person,?ConsoleApp5]] 00007ff7f27c7a10????????3??????????136?ConsoleApp5.Person[] 00007ff7f27c2ad0????????3???????????96?ConsoleApp5.Person

從最后一行輸出可以看到: ConsoleApp5.Person 的 Count=3,也就表明沒有所謂的深copy,如果你還不信的話,可以在 query 中修改某一個Person的Age,看看原始的 personList 集合是不是同步更新,修改代碼如下:

static?void?Main(string[]?args){var?personList?=?new?List<Person>()?{new?Person()?{?Name="jack",?Age=20?},new?Person()?{?Name="elen",Age=25,??},new?Person()?{??Name="john",?Age=22?}};var?query?=?personList.Where(m?=>?m.Age?>?20).ToList();//故意修改 Age=25 為? Age=100;?query[0].Age?=?100;Console.WriteLine($"query[0].Age={query[0].Age},?personList[2].Age={personList[1].Age}");Console.ReadLine();}

從截圖來看更加驗證了 并沒有所謂的 深copy 一說。

3. 真的是 copy 引用嗎?

要驗證是不是 copy 引用,最粗暴的方法就是看看 query 這個數組在 托管堆上的存儲行態就明白了,同樣你也可以借助 windbg 去驗證一下,先到線程棧去找 query 變量,然后用 da 命令 對 query 進行打印。

0:000>?!clrstack?-l OS?Thread?Id:?0x809c?(0)Child?SP???????????????IP?Call?Site 000000E143D7E9B0?00007ff7f26f18be?ConsoleApp5.Program.Main(System.String[])?[E:\net5\ConsoleApp5\ConsoleApp5\Program.cs?@?20]LOCALS:0x000000E143D7EA38?=?0x00000218266aab700x000000E143D7EA30?=?0x00000218266aad980:000>?!do?0x00000218266aad98 Name:????????System.Collections.Generic.List`1[[ConsoleApp5.Person,?ConsoleApp5]] MethodTable:?00007ff7f27b2b60 EEClass:?????00007ff7f27abad0 Size:????????32(0x20)?bytes File:????????C:\Program?Files\dotnet\shared\Microsoft.NETCore.App\3.1.9\System.Private.CoreLib.dll Fields:MT????Field???Offset?????????????????Type?VT?????Attr????????????Value?Name 0000000000000000??4001c35????????8??????????????SZARRAY??0?instance?00000218266aadb8?_items 00007ff7f26bb1f0??4001c36???????10?????????System.Int32??1?instance????????????????2?_size 00007ff7f26bb1f0??4001c37???????14?????????System.Int32??1?instance????????????????2?_version 0000000000000000??4001c38????????8??????????????SZARRAY??0???static?dynamic?statics?NYI?????????????????s_emptyArray0:000>?!da?00000218266aadb8 Name:????????ConsoleApp5.Person[] MethodTable:?00007ff7f27b7a10 EEClass:?????00007ff7f26b6580 Size:????????56(0x38)?bytes Array:???????Rank?1,?Number?of?elements?4,?Type?CLASS Element?Methodtable:?00007ff7f27b2ad0 [0]?00000218266aac00 [1]?00000218266aac20 [2]?null [3]?null

從最后四行代碼可以看出數組有 4 個格子,前2個格子放的是內存地址,后兩個都是 null,可能有些朋友會問,query 不是 2 條記錄嗎?怎么會有 4 個格子呢?這是因為 query 是 List 結構,而 List 底層用的是數組,默認以 4 個格子起步,不信的話翻一下 List 原代碼即可。

public?class?List<T>{private?void?EnsureCapacity(int?min){if?(_items.Length?<?min){int?num?=?(_items.Length?==?0)???4?:?(_items.Length?*?2);???//默認?4?個大小if?((uint)num?>?2146435071u){num?=?2146435071;}if?(num?<?min){num?=?min;}Capacity?=?num;}}}

如果你想進一步查看數組中前兩個元素 00000218266aac00, 00000218266aac20 指向的是什么,可以用 !do 打印一下即可。

0:000>?!do?00000218266aac00 Name:????????ConsoleApp5.Person MethodTable:?00007ff7f27b2ad0 EEClass:?????00007ff7f27c2a00 Size:????????32(0x20)?bytes File:????????E:\net5\ConsoleApp5\ConsoleApp5\bin\Debug\netcoreapp3.1\ConsoleApp5.dll Fields:MT????Field???Offset?????????????????Type?VT?????Attr????????????Value?Name 00007ff7f2771e18??4000001????????8????????System.String??0?instance?00000218266aab30?<Name>k__BackingField 00007ff7f26bb1f0??4000002???????10?????????System.Int32??1?instance???????????????25?<Age>k__BackingField 0:000>?!do?00000218266aac20 Name:????????ConsoleApp5.Person MethodTable:?00007ff7f27b2ad0 EEClass:?????00007ff7f27c2a00 Size:????????32(0x20)?bytes File:????????E:\net5\ConsoleApp5\ConsoleApp5\bin\Debug\netcoreapp3.1\ConsoleApp5.dll Fields:MT????Field???Offset?????????????????Type?VT?????Attr????????????Value?Name 00007ff7f2771e18??4000001????????8????????System.String??0?instance?00000218266aab50?<Name>k__BackingField 00007ff7f26bb1f0??4000002???????10?????????System.Int32??1?instance???????????????22?<Age>k__BackingField

到這里為止,我覺得回答這位朋友的疑問應該是沒有問題了,不過這里既然說到了集合中的引用類型,不得不說一下集合中的值類型又會是怎么樣的?

三:集合中的值類型是什么樣的copy方式

1. 使用 windbg 驗證

有了上面的基礎,驗證這個問題的答案就簡單了,先上測試代碼

static?void?Main(string[]?args){var?list?=?new?List<int>()?{?1,?2,?3,?4,?5,?6,?7,8,9,10?};var?query?=?list.Where(m?=>?m?>?5).ToList();Console.ReadLine();}

然后直接把整個數組內容打印出來

//?list 0:000>?!DumpArray?/d?0000019687c8aba8 Name:????????System.Int32[] MethodTable:?00007ff7f279f090 EEClass:?????00007ff7f279f010 Size:????????88(0x58)?bytes Array:???????Rank?1,?Number?of?elements?16,?Type?Int32 Element?Methodtable:?00007ff7f26cb1f0 [0]?0000019687c8abb8 [1]?0000019687c8abbc [2]?0000019687c8abc0 [3]?0000019687c8abc4 [4]?0000019687c8abc8 [5]?0000019687c8abcc [6]?0000019687c8abd0 [7]?0000019687c8abd4 [8]?0000019687c8abd8 [9]?0000019687c8abdc [10]?0000019687c8abe0 [11]?0000019687c8abe4 [12]?0000019687c8abe8 [13]?0000019687c8abec [14]?0000019687c8abf0 [15]?0000019687c8abf4//?query 0:000>?!DumpArray?/d?0000019687c8ae68 Name:????????System.Int32[] MethodTable:?00007ff7f279f090 EEClass:?????00007ff7f279f010 Size:????????56(0x38)?bytes Array:???????Rank?1,?Number?of?elements?8,?Type?Int32 Element?Methodtable:?00007ff7f26cb1f0 [0]?0000019687c8ae78 [1]?0000019687c8ae7c [2]?0000019687c8ae80 [3]?0000019687c8ae84 [4]?0000019687c8ae88 [5]?0000019687c8ae8c [6]?0000019687c8ae90 [7]?0000019687c8ae94

仔細對比 list 和 query 的數組呈現,發現有兩點好玩的信息:

  • 值類型和引用類型一樣,數組中都是存放地址的。

  • 值類型數組中的所有格子都被填滿,不像引用類型數組中還有 null 的情況。

接下來的問題是,數組中每個元素的地址到底指向了誰,可以挑出每個數組的 0 號元素地址,用 dp 命令看一看:

//list 0:000>?dp?0000019687c8abb8 00000196`87c8abb8??00000002`00000001?00000004`00000003 00000196`87c8abc8??00000006`00000005?00000008`00000007 00000196`87c8abd8??0000000a`00000009?00000000`00000000//query 0:000>?dp?0000019687c8ae78 00000196`87c8ae78??00000007`00000006?00000009`00000008 00000196`87c8ae88??00000000`0000000a?00000000`00000000

看到沒有,原來地址上面存放的都是數字值,深copy無疑哈。

四:總結

以上所有的分析可以得出:引用類型數組是引用copy,值類型數組是深copy,有時候背誦得來的東西總是容易忘記,只有實操驗證才能真正的刻骨銘心!????????????

總結

以上是生活随笔為你收集整理的linq 查询的结果会开辟新的内存吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。