一、值類型和引用類型的定義和區(qū)別
1.值類型包括基礎(chǔ)類型(int坞生、float仔役、bool)、枚舉類型enum是己、結(jié)構(gòu)體類型struct又兵。派生自System.ValueType(繼承Object)。
? 引用類型包括類Class沛厨、接口Interface、委托delegate摔认、數(shù)組ArrayList、字符串String参袱。派生自O(shè)bject。
? 擴(kuò)展:ValueType重寫了Equals()方法蓖柔,從而對值類型按照實(shí)例的值來比較,而不是引用地址來比較况鸣。
? Vector3牢贸、Quaternion是值類型潜索,GameObject、Transform是引用類型懂酱。
2.值類型存儲的是變量實(shí)際的值,引用類型存儲的是變量的內(nèi)存地址列牺,指向托管堆內(nèi)存。
3.值類型存儲在棧上,引用類型存儲在托管堆上(地址存在棧上)泌辫。
? ?擴(kuò)展:棧是有序、連續(xù)的內(nèi)存域震放,由系統(tǒng)自動分配和維護(hù)宾毒,需要在編譯期間預(yù)先分配好內(nèi)存大小。
? ? ? ? ? ? ?堆是無序殿遂、不連續(xù)的內(nèi)存域诈铛,由用戶自己控制釋放或者觸發(fā)GC。
4.值類型在賦值時墨礁,會生成獨(dú)立的數(shù)據(jù)副本幢竹,修改新值時,舊的變量不受影響饵溅。
? 引用類型在賦值時妨退,傳遞的是內(nèi)存地址,新數(shù)據(jù)和舊數(shù)據(jù)指向同一個托管堆數(shù)據(jù)蜕企,修改任意一個值時咬荷,另一個也會變化。
5.值類型不可以派生轻掩,不可以為空幸乒。引用類型可以派生,可以為空唇牧。
二罕扎、裝箱和拆箱
裝箱和拆箱的定義:
裝箱是值類型轉(zhuǎn)換為引用類型的過程 ;
拆箱是引用類型轉(zhuǎn)換為值類型的過程。
int value = 10;
object obj = value;? ? ? ? ? ? ? ? ?//裝箱
int newValue = (int)obj;? ? ? ? ? //拆箱
裝箱和拆箱的內(nèi)存操作:
裝箱操作:
1.生成一個新的引用類型丐重,在托管堆中分配內(nèi)存腔召。(分配內(nèi)存)
2.將值類型數(shù)據(jù)拷貝到分配的內(nèi)存中。(數(shù)據(jù)拷貝)
3.返回托管堆對象的地址扮惦。
拆箱操作:
1.獲取托管堆中值類型部分字段的地址臀蛛。
2.將引用對象的值拷貝到棧上的新實(shí)例中。
什么時候會發(fā)生裝箱/拆箱:
1.一個包含參數(shù)類型為Object的方法崖蜜,調(diào)用該方法時傳入了值類型參數(shù)浊仆,會發(fā)生裝箱。
2.使用非泛型容器(如ArrayList)豫领,將值類型加入容器時抡柿,會發(fā)生裝箱。
如何避免裝箱和拆箱的性能開銷:
裝箱操作會生成新的引用對象并賦值等恐,并且造成GC洲劣,會造成較大的開銷备蚓,因此需要盡量避免:
1.針對上述情況1,使用重載方法避免裝箱闪檬。
2.針對上述情況2星著,使用泛型容器避免裝箱。
3.對于多次裝箱操作粗悯,可以考慮提前進(jìn)行顯式裝箱,減少裝箱次數(shù)同欠。
三样傍、值類型和引用類型的嵌套
1.值類型嵌套定義引用類型(struct結(jié)構(gòu)包含class):
? ?值類型嵌套定義引用類型時衫哥,棧上將保存該引用類型的地址襟锐,而實(shí)際的數(shù)據(jù)則依然保存在托管堆中。
? ?//值類型嵌套定義引用類型的情況
????public struct Temp
????{
????????//結(jié)構(gòu)體字段蚊荣,注意:結(jié)構(gòu)體中字段不能被初始化
????????private TestClass testClass;
????????//結(jié)構(gòu)體的構(gòu)造函數(shù)互例,注意:結(jié)構(gòu)體中不能顯式定義無參的構(gòu)造函數(shù)
????????public Temp(TestClass t)
????? ? {
????????if(t ==null)
????????????thrownewArgumentNullException("t");
? ? ? ? testClass = t;
? ? ? ? testClass.x =10;
? ? ? ? testClass.y =20;
? ? }
}
2.引用類型嵌套定義值類型(class包含值類型):
? ?i.類的字段類型是值類型媳叨,它將作為引用類型實(shí)例的一部分关顷,被分配到托管堆中议双。
? ?ii.但那些作為局部變量的值類型,則仍然會被分配到線程棧中夫偶。
public classTest
{
????// num作為引用類型的一部分被分配到托管堆上
????private int num =10;
????public void Temp()
? ? {
????????// d被分配到線程棧上
????????double d =3.14;
? ? }
}