跳到主要內容

Reference Type & Value Type



相信有仔細看過前一篇文章後

對於C#中Value Type 與Ref  Type有了一些初步的了解(才怪)

今天直接 Passing By Value 與 Passing By Reference來探討Value Type 與Ref  Type的差別

話不多說舉個例子


1.參考型別(Reference Type)



假設某個學校出了一個模範生小明,老師希望以後所有的學生都像小明一樣聰明

所以把小明克隆了一個新的取名叫做小華

看以下程式碼
 public static void Main()
        {
            var student1 = new Stutent();
            student1.Name = "阿明";
            var student2 = student1;
            student2.Name = "小華";
            Console.WriteLine(student1.Name);
        }
        class Stutent
        {
            public string Name{get;set;}
        } 
猜猜Output出什麼呢

答案是小華

什麼?

為什麼?

靠北不是把令了一個新的變數student2來裝student1了嘛

那為什麼把新生出來的student2的名字取為小華

導致原本的小明也變成小華了呢

老師感到疑惑,小明到底跑哪去了呢,聰明的老師立刻打開電腦

Google了Msdn才發現   阿~!  小華原來就是小明 還裝?!

因為 Class Student是一個參考型別

參考型別的變數承載的是記憶體的位置


將一個新的座位,指派給小明,

並把新座位的主人改名為小華,

然後在請原本小明座位上的人大聲喊出自己的名字

小明 當然就會說 "我是小華"


仔細看看這段程式碼
var student1 = new Stutent();

再把它中文翻譯

我宣告了一個變數student1 並指派為一個新的學生 口語邏輯上是這樣講

但在參考型別的情況下實際上背後做了什麼事呢?

其實這邊做了兩件事情


  1. 左式宣告變數時  會在stack記憶體區域中增加一個student1變數的值,而student1紀錄的是這個物件的位置
  2. 右式指派變數時 則會在student1所指定Heap記憶區塊中產生真正的Object內容並將這個Object所在的Heap位置傳給變數student1作為他的參考值


所以總歸結論(附上專有名詞以供Google)

只是把小明的座位指派給小華是不行的。

  1. 使用class關鍵字宣告的型別為參考型別(reference type)。
  2. 參考型別的變數(variables)的值(value)儲存的是實體(instance)的位址而不是實體本身。
  3. 參考型別的參數傳遞的行為還是傳遞值,但變數的值裝載是實體的址。


2.實質型別(Value Type)


相對於參考型別的變數值儲存的是址,實質型別的變數的值儲存的是確確實實的個體

public static void Main()
        {
            var student1 = new Student();
            student1.Name = "阿明";
            var student2 = student1;
            student2.Name = "小華";
            //output "阿明"
            Console.WriteLine(student1.Name);
            //output "小華"
            Console.WriteLine(student2.Name);
            Console.ReadKey();
        }
        public struct Student
        {
            public string Name { get; set; }
        }

現在可以看到student1與student2都是獨立的個體了,

因為實質型別的變數傳遞是傳值(passing by value)
var student2 = student1;
這時候上面這一句宣告背後做的事情
就是小明自體分裂後成為一個新的小明2號並進入老師指派student2軀殼裡

實際上的行為是在Stack記憶區中建立student2的變數並student1的值複製後傳進此變數。

實質型別除了使用Struct關鍵字外宣告,

C#中已經內建宣告了15種實質型別   請參照 實質型別表

所以在此做一個簡單的實質型別結論:


  1. 實質型別(value type)的類別宣告(declare)使用Struct關鍵字(keyword)。
  2. 變數直接儲存目標的值。
  3. 實質型別的變數的傳遞,是將本身複製一份並傳遞進變數。


小小心得
若有謬誤
歡迎底下留言指正討論



參考 https://www.microsoftpressstore.com/articles/article.aspx?p=2454676

留言

這個網誌中的熱門文章

C# 委派(Delegate) (一)

今天要介紹的是C#的委派 Delegate Delegate的發展由函數指標( funtion pointer )而來,可以說Delegate是函數指標中的語法糖( Syntactic sugar )也不為過,在Java中則是Sam類。早期在C# 1.0的時候,匿名函數與Lambda表達式還沒有被發展出來,C與C++有函數指標來負責打包函數( function ),那C#呢?我們都知道物件導向程式OOP中所有的東西都該是物件,所以C#就創造了一個類別專門來打包函數。 本篇開始會依序從C#的發展順序介紹委派 Delegate、Anonymous Function、 Lambda  Expression、Func<>、Action<> 委派含括很大的範圍,故可能會花上幾篇的篇幅來一一介紹。   1.Delegate 首先來介紹的是最初的委派( Delegate )。月亮是外國的圓,我們先來看看微軟( MicroSoft )怎麼介紹自家的的委派-- A delegate is a reference type that can be used to encapsulate a named or an anonymous method -- 一種用來封裝具名或匿名方法的參考 類型 。至於為什麼封裝函數叫做委派呢,不如試想,原本有一個流程方法(Sop)是要由A公司去完成的,但我們將他外包給委派給B公司去做,把方法流程外包的行為就叫做委派,這樣不難理解吧。 廢話不多說,立刻來看委派是如何被宣告( declare )以及使用的。 委派的宣告: public delegate int MyDelegete(int x); 我們可以注意到一個委派類型的宣告簽章( Signatures ),就像是類似宣告一個方法般,給他回傳型態、名稱、參數就完成宣告了,所以我們先做的是宣告一個回傳型態為int、委派類型名稱為Mydelegate、參數型態為int的委派宣告。好啦,宣告完成拉接下來是封裝方法。既然委派是一種類型,所以我們必須將他實作( implement )。 挖哩怎麼報紅了,原來委派類型變數的宣告必須在建構子參數中給與這個符合此委派類型簽章的方法,於是我們必須相應而生出一個回傳型態、參數型態皆

C# 委派(Delegate) (二) - Anonymous Function、Lambda Expression

上一篇中 C# 委派(Delegate) (一) 介紹了Delegate類基本觀念、宣告方式與使用方式,本篇將照著C#中委派的發展史中,繼續介紹 Anonymous Function、Lambda Expression。 Anonymous Function 還記得上篇的範例中,我們每宣告一個委派( delegate )的變數( variable )就要寫一個跟委派相同簽章的 具名 方法對應是吧? 但是這個專用來給委派變數宣告用 具名 方法, 具名 不是顯得很多餘嗎,我們似乎不太需要知道這個方法叫什麼名字啊。 public delegate int MyDelegate(int x); static void Main(string[] args) { //AddOne是專門寫來給委派變數使用的方法,這個具名方法只用一次,從此以後AddOne沒在被用過。 MyDelegate mydelegate = new MyDelegate(AddOne); } public static int AddOne(int number) { return ++number; } 所以在C#2.0中貼心的微軟發展了一套專門給委派變數宣告專用的語法糖( Syntactic sugar )叫做匿名方法( Anonymous Function )。 public delegate int MyDelegate(int x); static void Main(string[] args) { //不需要再寫一個AddOne方法 MyDelegate mydelegate = delegate (int number) { return number + 1; }; } 所以在C#2.0中只要如上宣告就完成了委派變數的宣告。匿名方法的變數宣告中,左式必須要使用明確的委派類別來宣告(這邊使用 MyDelegate ),不能使用 var 關鍵字,如果堅持要使用var關