C#值型別與引用型別在使用上的區別
- 值型別與引用型別
為了探明兩者區別,直接看程式碼:
public class Object_1 { private int m_Age; public int Age { get { return m_Age; } set { m_Age = value; } } private string m_Namr; public string Name { get { return m_Namr; } set { m_Namr = value; } } } public struct Struct_1 { private int m_Age; public int Age { get { return m_Age; } set { m_Age = value; } } private string m_Namr; public string Name { get { return m_Namr; } set { m_Namr = value; } } }
在上面我們定義了一個類(Object_1)和一個結構體(Struct_1)。我們都知道類是引用型別,而結構體是值型別,所以接下來進行對比,我們讓兩者在Change()方法中改變值:
private void Form1_Load(object sender, EventArgs e) { Object_1 obj = new Object_1(); obj.Age = 1; obj.Name = "Mike"; Struct_1 stru = new Struct_1(); stru.Age = 2; stru.Name = "Tim"; Change(obj,stru); MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name); } public void Change(Object_1 obj_1, Struct_1 stru_1) { obj_1.Age += 10; obj_1.Name += "_Obj"; stru_1.Age += 10; stru_1.Name += "_Stru"; }
得到結果:
可以發現,obj.Age與obj.Name的值發生了改變,但是stru.Age與stru.Name的值依然與之前一樣。
在Change()方法內部obj_1與stru_1的值都被改變,但是為什麼在執行完Change()之後卻出現差異。那可以設想為我們所改變的stru_1實際上不是真正的源引數。查詢資料後得知:
那是因為在Change()方法內部obj_1被視為與傳進obj引數是一個物件,而stru_1只是將引數stru的值Copy了一份。所以我們在內部修改stru_1的值只是修改了這個“替身”的值,本體的值並未修改。
所以也可以得出結論:
值型別在方法體內作為引數被時,只是將值Copy一份使用,因而對其所做操作都無法對源引數產生影響。
引用型別在方法體內被作為引數時,內部物件與引數物件一致,對其所做的操作會影響到源引數。
PS:int型,long型,char型等資料型別都是struct,所以符合我們上面的結論。
- ref與out
繼續深入:
有時,我們也希望能將值型別像引用型別一樣使用,比如在方法內修改值型別也影響源引數。
C#內相應提供了兩個關鍵詞:ref,out (兩者區別參考這裡)
當使用這兩個關鍵詞之後,我們看看有什麼變化,修改上面的程式碼如下:
private void Form1_Load(object sender, EventArgs e)
{
Object_1 obj = new Object_1();
obj.Age = 1;
obj.Name = "Mike";
Struct_1 stru = new Struct_1();
stru.Age = 2;
stru.Name = "Tim";
Change(obj,ref stru);//此處被修改
MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name);
}
public void Change(Object_1 obj_1, ref Struct_1 stru_1)//此處被修改
{
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
}
結果如下:
stru.Age與stru.Name的值發生了變化。這也達到了我們需求,值型別也可以像引用型別一樣使用。
但是原因是什麼?
原因在於方法體在執行時,編譯器根據ref關鍵詞將值型別與引數物件“等同”了起來,而不是將值Copy一份。
這所有的一切究其原因,是在方法體內有種機制,這種機制將引數加上"簽名"(可以理解為與身份證號類似的唯一標識),當是引用型別時,簽名與外側引數相同,值型別時則使用不同的“簽名”。除非使用了ref或out關鍵詞,值型別的“簽名”才與源引數一致。
- new物件
剛剛我們說的都是改變值,若是將new物件呢?繼續修改上面Change()方法程式碼如下:
public void Change(Object_1 obj_1, ref Struct_1 stru_1)
{
obj_1 = new Object_1();//此處被修改
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
}
結果如下:
由於在Change()內obj_1物件被new了,所以新物件的“簽名”與源引數的“簽名”已經不同,所以方法內部值的改變不影響原來的物件。如果要使new物件影響源引數,依然可以使用ref。
public void Change(ref Object_1 obj_1, ref Struct_1 stru_1)
- string型別
有人說string型別是特殊的引用型別,因為它與值型別一樣在方法內部改變後無法影響原引數,為了驗證,繼續修改程式碼如下:
private void Form1_Load(object sender, EventArgs e)
{
Object_1 obj = new Object_1();
obj.Age = 1;
obj.Name = "Mike";
Struct_1 stru = new Struct_1();
stru.Age = 2;
stru.Name = "Tim";
string str = "A";//此處被修改
Change(ref obj, ref stru, str);
MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name + "\nstr:" + str);//此處被修改
}
public void Change(ref Object_1 obj_1, ref Struct_1 stru_1, string str)//此處被修改
{
obj_1 = new Object_1();
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
str = "B";//此處被修改
}
結果如下:
表面上str的值沒有改變,依然是“A”。但是不要只看表面,之所以依然顯示為“A”,,是因為在這裡string=運算子實際上等同於str=new String(new char[]{'B'});也就是new了一個新物件, 那str的“簽名”也自然就與源引數不同,也就不會影響源引數(與 obj_1 = new Object_1()是同樣的道理)。
相關推薦
C#值型別與引用型別在使用上的區別
值型別與引用型別 為了探明兩者區別,直接看程式碼: public class Object_1 { private int m_Age; public int Age { get
C#中值型別與引用型別的區別
值型別是直接儲存一個數值,而引用型別是儲存對值的引用,這兩種型別分別儲存在不用的記憶體區域。而從記憶體上看,值型別是在棧中的操作,而引用型別是在堆中的操作。值型別是具體的那個數值所佔用的空間大小,而引用
值型別與引用型別的區別
值型別 struct結構,enum列舉 結構包括: 簡單結構(int,char,float,double,bool.....),使用者自定義的結構體, 引用型別 類,陣列,介面,代理 類包括 object,string,使用者自定義類 區別 (1)值型別存在棧上
值型別與引用型別之間的區別
值型別:是指直接將記憶體儲存在棧內,由系統自動釋放資源的資料型別. 引用型別:是指由型別的實際值引用表示的資料型別. 兩者直接的區別在於值型別儲存具體的值,引用型別儲存值的地址 值型別: 例如:var a=1,b=2; b++; 這時b為3,a為1 引用型別:
C#中的棧和堆、值型別與引用型別、值引數、引用引數、輸出引數、引數陣列
程式執行時,資料必須儲存在記憶體中,一個數據需要多大的記憶體、儲存的位置、如何儲存依賴於該資料的資料型別。執行中的程式使用兩個記憶體區域來儲存資料:棧和堆。 棧: 棧是一
golang 值型別與引用型別的區別
1最本質的區別 值型別:記憶體中變數儲存的是具體的值 比如: var num int num存放的是具體的int值 但是變數在記憶體中的地址可以通過 &num 來獲取 引用型別:變數直接存放的就是一個地址值,這個地址值指向的空間存的才是值。 例如 va
C#學習筆記(5)-值型別與引用型別
值型別 值型別的值存在棧上 int double char decimal bool enum struct 引用型別 引用型別存在堆上 - string - 陣列 - 自定義類 - 集合 - object - 介面
C# 淺析值型別與引用型別的記憶體分配
1、 值型別和引用型別的區別 1.值型別的資料儲存在記憶體的棧中;引用型別的資料儲存在記憶體的堆中,而記憶體單元中只存放堆中物件的地址。 2. 值型別存取速度快,引用型別存取速度慢。 3. 值型別表示實際資料,引用型別表示指向儲存在記憶體堆中的資料的指標
反射中使用值型別與引用型別的引數的區別
程式 : public class ClassExample { public string myString; } public struct StructExample { public string myString;
Python與JavaScript對比:值型別與引用型別
終於鼓起勇氣學習Python了,簡單做些筆記 Python值型別:Number、str、tuple、num 等 a = 2 b = a a = 3 修改值型別的值,只是讓它指向一個新的記憶體地址,並不會改變變數b的值 -----------------------
淺析值型別與引用型別的記憶體分配[轉載]
1、值型別和引用型別的區別 1.值型別的資料儲存在記憶體的棧中;引用型別的資料儲存在記憶體的堆中,而記憶體單元中只存放堆中物件的地址。 2. 值型別存取速度快,引用型別存取速度慢。 3. 值型別表示實際資料,引用型別表示指向儲存在記憶體堆中的資料的指標或引用
值型別與引用型別及其物件複製
引言 本文之初的目的是講述設計模式中的 Prototype(原型)模式,但是如果想較清楚地弄明白這個模式,需要了解物件克隆(Object Clone),Clone其實也就是物件複製。複製又分為了淺度複製(Shallow Copy)和深度複製(Deep Copy),淺度複製 和 深度複製又是以
js中值的基本型別與引用型別,以及物件引用,物件的淺拷貝與深拷貝
js有兩種型別的值:棧:原始資料型別(undefinen,null,boolead,number,string)堆:引用資料型別(物件,函式和陣列)兩種型別的區別是:儲存位置不同,原始資料型別直接儲存在棧(stack)中的簡單資料段,佔據空間小,大小固定,屬於被頻繁使用的資料,所以放入棧中儲存;引用資料型別儲
值型別與引用型別淺析
1、 總概括: 值型別就是現金,要用直接用;引用型別是存摺,要用還得先去銀行取現。 2、值型別與引用型別簡介 (1)C#的所有值型別均派生自System.ValueType: 結構體: 數值型別:整型(by
C#中值傳遞與引用傳遞的區別
以值傳遞引數 當實參當作值來傳遞時,就產生了一個新的拷貝。 class Test { static void Main(string[] args) { int x=8; Fo(x);
C++傳值呼叫與引用呼叫的區別
簡單來說,傳值呼叫就是指當一個函式被呼叫時,C++根據實參和形參的對應關係將實參的值一一複製給形參,即實參的值單向傳遞給形參。函式本身不對實參進行任何操作,即使形參的值在函式中改變,實參的值也不會受到影響。 引用呼叫過程中,被調函式的形式引數雖
第九回:品味型別---值型別與引用型別(中)-規則無邊
本文將介紹以下內容: 型別的基本概念 值型別深入 引用型別深入 值型別與引用型別的比較及應用 1. 引言 上回[第八回:品味型別---值型別與引用型別(上)-記憶體有理]的釋出,受到大家的不少關注,我們從記憶體的角度瞭解了值型別和引用型別的所以然,留下的任務當然是
深入解析js中基本資料型別與引用型別,函式引數傳遞的區別
ECMAScript的資料有兩種型別:基本型別值和引用型別值,基本型別指的是簡單的資料段,引用型別指的是可能由多個值構成的物件。 Undefined、Null、Boolean、Number和String是值型別,其他都是引用型別。其他語言String是
.NET中的值型別與引用型別
.NET中的值型別與引用型別 這是一個常見面試題,值型別(Value Type)和引用型別(Reference Type)有什麼區別?他們效能方面有什麼區別? TL;DR(先看結論) 值型別 引用型別 建立位置 棧 託管堆 賦值時 複製值 複製引用 動態記憶體分配 無 需要分配記憶體
js中的棧與堆的講解/基本資料型別與引用型別的講解
1、棧(stack)和堆(heap) stack為自動分配的記憶體空間,它由系統自動釋放;而heap則是動態分配的記憶體,大小不定也不會自動釋放。 2、基本型別和引用型別 基本型別:存放在棧記憶體中的簡單資料段,資料大小確定,記憶體空間大小可以分配。 5種基