c# 參考型別與實值型別的區別詳解

來源:互聯網
上載者:User

解析:CLR支援兩種類型:實值型別和參考型別。用Jeffrey Richter(《CLR via C#》作者)的話來說,“不理解參考型別和實值型別區別的程式員將會把代碼引入詭異的陷阱和諸多效能問題”。這就要求我們正確理解和使用實值型別和參考型別。
實值型別包括C#的基本類型(用關鍵字int、char、float等來聲明),結構(用struct關鍵字聲明的類型),枚舉(用enum關鍵字聲明的類型);而參考型別包括類(用class關鍵字聲明的類型)和委託(用delegate關鍵字聲明的特殊類)。
C#中的每一種類型要麼是實值型別,要麼是參考型別。所以每個對象要麼是實值型別的執行個體,要麼是參考型別的執行個體。實值型別的執行個體通常是線上程棧上分配的(靜態分配),但是在某些情形下可以儲存在堆中。參考型別的對象總是在進程堆中分配(動態分配)。
(1)在C#中,變數是值還是引用僅取決於其基礎資料型別 (Elementary Data Type)。
C#的基礎資料型別 (Elementary Data Type)都與平台無關。C#的預定義類型並沒有內建於語言中,而是內建於.NET Framework中。.NET使用一般型別系統(CTS)定義可以在中繼語言(IL)中使用的預定義資料類型。C#中所有的資料類型都是對象。它們可以有方法、屬性等。例如,在C#中聲明一個int變數時,聲明實際上是CTS(一般型別系統)中System.Int32的一個執行個體: 複製代碼 代碼如下:int i;
i = 1;
string s;
s = i.ToString();

(2)System.Object和System.ValueType。
參考型別和實值型別都繼承自System.Object類。不同的是,幾乎所有的參考型別都直接從System.Object繼承,而實值型別則繼承其子類,即直接繼承System.ValueType。作為所有類型的基類,System.Object提供了一組方法,這些方法在所有類型中都能找到。其中包含toString方法及clone等方法。System.ValueType繼承System.Object。它沒有添加任何成員,但覆蓋了所繼承的一些方法,使其更適合於實值型別。
(3)實值型別。
C#的所有實值型別均隱式派生自System.ValueType:
結構體:struct(直接派生於System.ValueType)。
數實值型別:整型,sbyte(System.SByte的別名),short(System.Int16),int(System.Int32),long(System.Int64),byte(System.Byte),ushort(System.UInt16),uint(System.UInt32),ulong(System.UInt64),char(System.Char)。
浮點型:float(System.Single),double(System.Double)。
用於財務計算的高精度decimal型:decimal(System.Decimal)。
bool型:bool(System.Boolean的別名)。
使用者定義的結構體(派生於System.ValueType)。
枚舉:enum(派生於System.Enum)。
可空類型。
每種實值型別均有一個隱式的預設建構函式來初始化該類型的預設值。例如:複製代碼 代碼如下:int i = 0;
等價於:
int i = new int();

使用new運算子時,將調用特定類型的預設建構函式並對變數賦予預設值。在上例中,預設建構函式將值0賦給了i。
所有的實值型別都是密封(seal)的,所以無法派生出新的實值型別。
值得注意的是,System.ValueType直接派生於System.Object。即System.ValueType本身是一個類類型,而不是實值型別。其關鍵在於ValueType重寫了Equals()方法,從而對實值型別按照執行個體的值來比較,而不是引用地址來比較。可以用Type.IsValueType屬性來判斷一個類型是否為實值型別:複製代碼 代碼如下:TestType testType = new TestType ();
if (testTypetype.GetType().IsValueType)
{
Console.WriteLine("{0} is value type.", testType.ToString());
}

(4)參考型別
C#有以下一些參考型別:
數組(派生於System.Array)
使用者需定義以下類型。
類:class(派生於System.Object);
介面:interface(介面不是一個“東西”,所以不存在派生於何處的問題。介面只是表示一種contract約定[contract])。
委託:delegate(派生於System.Delegate)。
object(System.Object的別名);
字串:string(System.String的別名)。
可以看出:
參考型別與實值型別相同的是,結構體也可以實現介面;參考型別可以派生出新的類型,而實值型別不能;參考型別可以包含null值,實值型別不能;參考型別變數的賦值只複製對象的引用,而不複製對象本身。而將一個實值型別變數賦給另一個實值型別變數時,將複製包含的值。
(5)記憶體配置。
實值型別的執行個體經常會儲存在棧上的。但是也有特殊情況。如果某個類的執行個體有個實值型別的欄位,那麼實際上該欄位會和類執行個體儲存在同一個地方,即堆中。不過參考型別的對象總是儲存在堆中。如果一個結構的欄位是參考型別,那麼只有引用本身是和結構執行個體儲存在一起的(在棧或堆上,視情況而定)。如下例所示:複製代碼 代碼如下:public struct ValueTypeStruct
{
private object referenceTypeObject;
public void Method()
{
referenceTypeObject = new object();
object referenceTypeLocalVariable = new object();
}
}
ValueTypeStruct valueTypeStructInstance = new ValueTypeStruct();
valueTypeStructInstance.Method();
//referenceTypeObject 和 referenceTypeLocalVariable 都在哪存放?
單看valueTypeStructInstance,這是一個結構體執行個體,感覺似乎是整塊都在棧上。但是欄位referenceTypeObject是參考型別,局部變數referenceTypeLocalVarible也是參考型別。
public class ReferenceTypeClass
{
private int _valueTypeField;
public ReferenceTypeClass()
{
_valueTypeField = 0;
}
public void Method()
{
int valueTypeLocalVariable = 0;
}
}
ReferenceTypeClass referenceTypeClassInstance = new ReferenceTypeClass();
// _valueTypeField在哪存放?
referenceTypeClassInstance.Method();
// valueTypeLocalVariable在哪存放?

referenceTypeClassInstance也有同樣的問題,referenceTypeClassInstance本身是參考型別,似乎應該整塊部署在託管堆上。但欄位_valueTypeField是實值型別,局部變數valueTypeLocalVariable也是實值型別,它們究竟是在棧上還是在託管堆上?
對上面的情況正確的分析是:參考型別在棧中儲存一個引用,其實際的儲存位置位於託管堆。為了方便,簡稱參考型別部署在託管堆上。實值型別總是分配在它聲明的地方,作為欄位時,跟隨其所屬的變數(執行個體)儲存;作為局部變數時,儲存在棧上。
(6)辨明實值型別和參考型別的使用場合。
在C#中,我們用struct/class來聲明一個類型為實值型別/參考型別。考慮下面的例子:
SomeType[] oneTypes = new SomeType[100];
如果SomeType是實值型別,則只需要一次分配,大小為SomeType的100倍。而如果SomeType是參考型別,剛開始需要100次分配,分配後數組的各元素值為null,然後再初始化100個元素,結果總共需要進行101次分配。這將消耗更多的時間,造成更多的記憶體片段。所以,如果類型的職責主要是儲存資料,實值型別比較合適。
一般來說,實值型別(不支援多態)適合儲存供 C#應用程式操作的資料,而參考型別(支援多態)應該用於定義應用程式的行為。通常我們建立的參考型別總是多於實值型別。如果滿足下面情況,那麼我們就應該建立為實值型別:
該類型的主要職責用於資料存放區。
該類型的共有介面完全由一些資料成員存取屬性定義。
該類型永遠不可能有子類。
該類型不具有多態行為。
答案:在C#中,變數是值還是引用僅取決於其資料類型。
C#的實值型別包括:結構體(數實值型別、bool型、使用者定義的結構體),枚舉,可空類型。
C#的參考型別包括:數組,使用者定義的類、介面、委託,object,字串。數組的元素,不管是參考型別還是實值型別,都儲存在託管堆上。
參考型別在棧中儲存一個引用,其實際的儲存位置位於託管堆。簡稱參考型別部署在託管推上。實值型別總是分配在它聲明的地方:作為欄位時,跟隨其所屬的變數(執行個體)儲存;作為局部變數時,儲存在棧上。實值型別在記憶體管理方面具有更好的效率,並且不支援多態,適合用做儲存資料的載體;參考型別支援多態,適合用於定義應用程式的行為。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.