c 引用型別和值型別區別

2021-06-08 18:03:07 字數 4180 閱讀 3298

解析:clr支援兩種型別:值型別和引用型別。用jeffrey richter(《clr via c#》作者)的話來說,「不理解引用型別和值型別區別的程式設計師將會把**引入詭異的陷阱和諸多效能問題」。這就要求我們正確理解和使用值型別和引用型別。

值型別包括c#的基本型別(用關鍵字int、char、float等來宣告),結構(用struct關鍵字宣告的型別),列舉(用enum關鍵字宣告的型別);而引用型別包括類(用class關鍵字宣告的型別)和委託(用delegate關鍵字宣告的特殊類)。

(1)在c#中,變數是值還是引用僅取決於其基本資料型別。

c#的基本資料型別都與平台無關。c#的預定義型別並沒有內置於語言中,而是內置於.net framework中。.net使用通用型別系統(cts)定義可以在中間語言(il)中使用的預定義資料型別。c#中所有的資料型別都是物件。它們可以有方法、屬性等。例如,在c#中宣告乙個int變數時,宣告實際上是cts(通用型別系統)中system.int32的乙個例項:

int i;

i = 1;

string s;

s = i.tostring();

下圖說明了cts中各個型別是如何相關的。

(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)

is value type.", testtype.tostring());

}(4)引用型別

c#有以下一些引用型別:

陣列(派生於system.array)

使用者需定義以下型別。

類:class(派生於system.object);

介面:inte***ce(介面不是乙個「東西」,所以不存在派生於何處的問題。介面只是表示一種contract約定[contract])。

委託:delegate(派生於system.delegate)。

object(system.object的別名);

字串:string(system.string的別名)。

可以看出:

引用型別與值型別相同的是,結構體也可以實現介面;引用型別可以派生出新的型別,而值型別不能;引用型別可以包含null值,值型別不能;引用型別變數的賦值只複製物件的引用,而不複製物件本身。而將乙個值型別變數賦給另乙個值型別變數時,將複製包含的值。

(5)記憶體分配。

值型別的例項經常會儲存在棧上的。但是也有特殊情況。如果某個類的例項有個值型別的字段,那麼實際上該字段會和類例項儲存在同乙個地方,即堆中。不過引用型別的物件總是儲存在堆中。如果乙個結構的字段是引用型別,那麼只有引用本身是和結構例項儲存在一起的(在棧或堆上,視情況而定)。如下例所示:

public struct valuetypestruct

}valuetypestruct valuetypestructinstance = new valuetypestruct();

valuetypestructinstance.method();

//referencetypeobject 和 referencetypelocalvariable 都在哪存放?

單看valuetypestructinstance,這是乙個結構體例項,感覺似乎是整塊都在棧上。但是欄位referencetypeobject是引用型別,區域性變數referencetypelocalvarible也是引用型別。

public class referencetypeclass

public void method()

}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,字串。陣列的元素,不管是引用型別還是值型別,都儲存在託管堆上。

引用型別在棧中儲存乙個引用,其實際的儲存位置位於託管堆。簡稱引用型別部署在託管推上。值型別總是分配在它宣告的地方:作為欄位時,跟隨其所屬的變數(例項)儲存;作為區域性變數時,儲存在棧上。值型別在記憶體管理方面具有更好的效率,並且不支援多型,適合用做儲存資料的載體;引用型別支援多型,適合用於定義應用程式的行為

值型別和引用型別區別

3.1.3 值型別和引用型別的區別 37 值型別和引用型別的基本概念 值型別和引用型別的記憶體分配 值型別的基類 分析問題 所有.net 的型別都可以分為兩類 值型別和引用型別。最簡單也最明確的乙個區分標準是 所有的值型別都繼承自 system.valuetype system.valuetype ...

值型別和引用型別區別

3.1.3 值型別和引用型別的區別 37 值型別和引用型別的基本概念 值型別和引用型別的記憶體分配 值型別的基類 分析問題 所有.net 的型別都可以分為兩類 值型別和引用型別。最簡單也最明確的乙個區分標準是 所有的值型別都繼承自 system.valuetype system.valuetype ...

引用型別和值型別區別

1.普通的資料型別,和結構體 列舉等,是值型別,賦值時,會建立乙個備份,給新的變數。2.類 物件是引用型別,賦值時,並不會新建物件的副本,而是把物件的位址給新物件名。所以,物件名中儲存的並不是完整物件的內容,只儲存了找到這個物件的位址。3.sets 集 3.1概念 相同型別 沒有固定順序 數值不能重...