Self Test - Value Type vs Reference Type

一個小測驗,請用大腦編譯並執行以下的程式,試著回答Q1-Q7的結果為何?

這個測驗可以檢定你是否對.NET的Value Type與Reference Type已有正確認識。

struct MyStruc 
{
    public string Name;
    public string Data;
    public MyStruc(string name, string data)
    {
        Name = name;
        Data = data;
    }
    public override string ToString()
    {
        return Name + ":" + Data;
    }
}
 
class MyClass
{
    public string Name;
    public string Data;
    public MyClass(string name, string data)
    {
        Name = name;
        Data = data;
    }
    public override string ToString()
    {
        return Name + ":" + Data;
    }
}
 
 
static void Func1(MyClass c)
{
    c.Data = "F1";
}
 
static void Func2(MyClass c)
{
    MyClass c2 = new MyClass("Foo", "F2");
    c = c2;
}
 
static void Func3(ref MyClass c)
{
    c.Data = "F3";
}
 
static void Func4(ref MyClass c)
{
    MyClass c2 = new MyClass("Foo", "F4");
    c = c2;
}
 
 
static void Func5(MyStruc s)
{
    s.Data = "F5";
}
 
static void Func6(MyStruc s)
{
    MyStruc s2 = new MyStruc("Bar", "F6");
    s = s2;
}
 
static void Func7(ref MyStruc s)
{
    s.Data = "F7";
}
 
static void VarTest()
{
    MyClass c = new MyClass("Init", "Null");
    Func1(c);
    Console.WriteLine("Q1=" + c.ToString());
    Func2(c);
    Console.WriteLine("Q2=" + c.ToString());
    Func3(ref c);
    Console.WriteLine("Q3=" + c.ToString());
    Func4(ref c);
    Console.WriteLine("Q4=" + c.ToString());
    MyStruc s = new MyStruc("Init", "Null");
    Func5(s);
    Console.WriteLine("Q5=" + s.ToString());
    Func6(s);
    Console.WriteLine("Q6=" + s.ToString());
    Func7(ref s);
    Console.WriteLine("Q7=" + s.ToString());
    Console.Read();
}

比較容易搞混的地方Func1及Func3,Class是Reference Type,所以不管是以By Value或By Reference的方式將物件變數傳入函數中,函數中存取及變更的都是你傳入的物件本尊,而不是另外複製的複本。但Func2, Func4倒是可以看出By Reference造成傳入函數的變數c有複本及本尊的差別。Structure在.NET中是Value Type,當成參數傳入時,除非用ref宣告By Reference,否則傳入的都是分身。

正確答案:
Q1=Init:F1
Q2=Init:F1
Q3=Init:F3
Q4=Foo:F4
Q5=Init:Null
Q6=Init:Null
Q7=Init:F7

PS:  昨天在寫程式時,遇到了Q2的Scenario,忽然迷惑了一下,就寫了一小段Code來確認,索性稍加修改變成一個小測驗讓大家玩一玩。

歡迎推文分享:
Published 19 October 2007 07:20 AM 由 Jeffrey
Filed under: ,
Views: 14,398



意見

# 阿良 said on 06 November, 2007 08:06 PM

請問c為何沒有被Func2更改

如你所說

class屬reference tyep

不論是by value or by reference

不是都該name=Foo, data=F2嗎

還是我哪裡搞錯了?

多謝賜教

# Jeffrey said on 07 November, 2007 01:05 AM

我把程式改一下(Func2裡用的c更名為tmpVar)

static void Func2(MyClass tmpVar)

{

   MyClass c2 = new MyClass("Foo", "F2");

   tempVar = c2;

}

在Func2中的tmpVar是個只活在Func2裡的變數,當你呼叫Func2(c)時,一開始tempVar與c都指向"Init,F1",但跑過之後的邏輯後,c仍指向"Init,F1",但tmpVar已改指向"Foo,F2",等Func2呼叫結束,tmpVar, "Foo,F2"的生命週期結束,c仍是指向"Init,F1"沒有任何改變。

# angel said on 17 March, 2010 06:37 AM

請問為何 func1   的c.Data 沒有隨著 func1 的結束 生命週期 結束 而卻去改變了  myClass的值

# Jeffrey said on 17 March, 2010 10:03 AM

to angel, MyClass是Class, 屬於Reference Type,因此Func1裡的參數c只是個指標,指向VarTest()裡建立的MyClass物件,也就是Func1裡c.Data="F1"時被修改的對象。當Func1結束,參數c的生命週期結束,但VarTest()裡建立的MyClass仍存在,而其中的Data值剛才在Func1裡被改掉了。

# kgame said on 06 November, 2012 08:37 AM

我相信C++底子好的人對這邊應該比較沒問題

C#中的MyClass在C++來看是MyClass*

C#的成員運算子 . 是C++的->

ref MyClass則是MyClass**

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<October 2007>
SunMonTueWedThuFriSat
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication