Self Test - Value Type vs Reference Type
5 |
一個小測驗,請用大腦編譯並執行以下的程式,試著回答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**