C# List的賦值問題的解決
問題描述
如下圖所示,query1是個集合,把它賦值給了query2,當移除query2里面數(shù)據(jù)的時候,query1對應(yīng)的數(shù)據(jù)也被移除了。
原因分析:
對此猜測是引用類型的問題,類似于淺拷貝深拷貝那種概念。
基礎(chǔ)概念:
對于上述的情況,要怎么去賦值,以及不同的寫法對應(yīng)的結(jié)果是什么樣的呢,我做了如下測試 ,在看結(jié)果之前,先了解一下相關(guān)概念。
C#中的堆和棧:(指的是程序運行時的內(nèi)存區(qū)域)
內(nèi)存分為堆區(qū)域和棧區(qū)域,??臻g比較小,但是讀取速度快(先進后出),堆空間比較大,但是讀取速度慢。
棧區(qū):存放函數(shù)的參數(shù),局部變量,返回數(shù)據(jù)等值,會自動釋放。
堆區(qū):存放著引用類型的對象,會由GC來自動釋放。
值類型和引用類型
值類型:在方法傳遞的時候,傳遞的是自身的“拷貝”。
(例如:結(jié)構(gòu)體struct,數(shù)據(jù)類型short/int/double/bool,枚舉類型enum,可空類型)引用類型:引用類型則是傳遞的自身的“地址”。
( 例如:數(shù)組,類,接口,委托,object,string)
值類型只需要一段單獨的內(nèi)存(此處指棧區(qū)內(nèi)存),用于存儲實際的數(shù)據(jù)。
引用類型需要兩段內(nèi)存,第一段存儲實際的數(shù)據(jù),位于堆中。第二段是一個引用,存儲在棧里,指向數(shù)據(jù)在堆中的存放位置。
特點:
1、值類型變量聲明后,不管是否已經(jīng)賦值,編譯器為其分配內(nèi)存。
2、引用類型當聲明一個類時,只在棧中分配一小片內(nèi)存用于容納一個地址,而此時并沒有為其分配堆上的內(nèi)存空間。當使用 new
創(chuàng)建一個類的實例時,分配堆上的空間,并把堆上空間的地址保存到棧上分配的小片空間中。
3、值類型的實例通常是在線程棧上分配的(靜態(tài)分配),但是在某些情形下可以存儲在堆中。(某些情況指的是:作為字段時,跟隨其所屬的變量(實例)存儲;作為局部變量時,存儲在棧上。)
4、引用類型的對象總是在進程堆中分配(動態(tài)分配)。
本地測試:
下圖展示了4個list以及對應(yīng)的賦值情況,從結(jié)果可以看出,list1、list3、list4的棧區(qū)內(nèi)容都指向了同一個堆地址,所以當這三個list任意一個刪除數(shù)據(jù)或添加數(shù)據(jù),其他兩個也會變化。而list2由于指向的堆地址跟其他三個不同,所以list2的數(shù)據(jù)變化不會影響到list1、list3、list4。
其中l(wèi)ist4比較出乎我的意料,印象中l(wèi)ist t=new list中的關(guān)鍵字new,會在托管堆上重新分配空間,并返回一個該空間的地址,但是從結(jié)果上來看,list4還是指向了list1的堆地址。猜測可能是編譯器的自動優(yōu)化?就像string的賦值那樣?
下面是string的測試,圖一是定義了三個變量,a、b、c,可以看出,a和b雖然是分別定義并且分別賦值的,但由于值一樣,最終還是指向了同一個堆地址。而圖二中,給a重新賦值后,發(fā)現(xiàn)它并沒有去更改原地址中的數(shù)據(jù),而是在堆中開辟了一塊新的空間,并指向了這個新的堆地址(在棧中的地址沒變)。
到此這篇關(guān)于C# List的賦值問題的解決的文章就介紹到這了,更多相關(guān)C# List賦值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#使用linq技術(shù)創(chuàng)建xml文件的小例子
c#使用linq技術(shù)創(chuàng)建xml文件的小例子,需要的朋友可以參考一下2013-03-03