一個小測驗,請用大腦編譯並執行以下的程式,試著回答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來確認,索性稍加修改變成一個小測驗讓大家玩一玩。


Comments

# by 阿良

請問c為何沒有被Func2更改 如你所說 class屬reference tyep 不論是by value or by reference 不是都該name=Foo, data=F2嗎 還是我哪裡搞錯了? 多謝賜教

# by Jeffrey

我把程式改一下(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"沒有任何改變。

# by angel

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

# by Jeffrey

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

# by kgame

我相信C++底子好的人對這邊應該比較沒問題 C#中的MyClass在C++來看是MyClass* C#的成員運算子 . 是C++的-> ref MyClass則是MyClass**

Post a comment