1. 程式人生 > >C#值型別與引用型別在使用上的區別

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;  

PythonJavaScript對比:型別引用型別

終於鼓起勇氣學習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種基