在上一篇Reference Type & Value Type中談到
Type分實質類型(Value Type) 與參考類型(Reference Type)
類型的分類除了以上兩種外還有另外一種較少看到或使用的指標類型(Pointer Type)
但指標類型跟我們最後要探討的目標比較無關,故在此先不予以討論。
C#中我們可以在方法參數的定義中
定義此方法是傳值(passing by value)方式還是傳址方式(passing by reference)
若是想要以傳址方式進行參數傳遞,
那在參數前面應該使用關鍵字(keyword) out 或是 ref
我們先以一個簡單的例子探討C#中方法傳值(Passing Parameters)的規則
public static void Main()
{
int i = 2;
Change(i);
Console.WriteLine(i); //Output 2
Change(ref i);
Console.WriteLine(i); //Output 4
Console.ReadKey();
}
public static int Change(int x)
{
var calculate = x*=x;
// Output 4
Console.WriteLine($"在方法內計算的結果為{calculate}");
return calculate;
}
public static int Change(ref int x)
{
var calculate = x *= x;
// Output 4
Console.WriteLine($"在方法內計算的結果為{calculate}");
return calculate;
}
- 第一種情況 -- 使用預設的方式進行參數傳遞
例子中我們可以明白到第一次使用int變數i的時候
雖然在方法內做完計算,但方法結束後變數i內部裝的仍然是原本的 2
經由上一篇的探討中我們可以明白因為int是value type
方法中的參數實際上做的是將原本i的值完完整整的複製成一個獨立個體並用這個副本傳遞
所以不難理解在方法內對獨立副本怎麼進行更動,都不會影響到外部本來的變數值。
- 第二種情況--使用ref傳址的方式進行參數傳遞
因為我們使用了關鍵字ref,所以這時候參數傳遞的方式不再是複製變數的值傳入
而是傳入參考(變數位址)本身,因為是直接對位址的內容物做事
所以這時候在方法內部的更動可以影響外部。
小試身手後,我們進入今天的重點,看看那在Class上的運用呢?
public static void Main()
{
var person = new Person { Name = "NiceMan" };
LearningToBad(person);
Console.WriteLine(person.Name); //Output BadMan
Console.ReadKey();
}
public static void LearningToBad(Person person)
{
person.Name = "BadMan";
}
很直覺的可以了解到因為Person是參考類型,我們已經對址內的內容物做改變,
所以即使以void方法去執行,也會對外部的實體Instance影響。
再看下一種情況,我們更動LearningToBad方法內的陳述式,
public static void Main()
{
var person = new Person { Name = "NiceMan" };
LearningToBad(person);
Console.WriteLine(person.Name); //Output NiceMan
Console.ReadKey();
}
public static void LearningToBad(Person person)
{
person = new Person { Name = "BadMan" };
}
奇怪的事情發生了,在內部讓person重生為一個壞人,但為什麼最終person沒有學壞?
原來是因為LearningToBad的內部區域變數person只是址的值。
person = new Person { Name = "BadMan" };
我們只是重新指派變數person的值為另一個位址的值,
所以理所當然的外部原本的變數person還是指向原本的Instance,
當然外部就不會有所改變
我們再重新更改方法參數的傳遞方式加上ref,試著將完整的址傳進參數裡
public static void Main()
{
var person = new Person { Name = "NiceMan" };
LearningToBad(ref person);
Console.WriteLine(person.Name); //Output BadMan
Console.ReadKey();
}
public static void LearningToBad(ref Person person)
{
person = new Person { Name = "BadMan" };
}
果然,外部的Instance重新被指向了新的壞人,你看看你,很壞唷。
綜觀以上,我們可以得知結論
方法參數的傳遞很容易混淆的是,不論參數類型是實質還是參考型別
方法參數都是以傳值(passing by value)方式傳遞
實際做的事情都是將原本的變數複製一份傳進方法內執行
而這時參數類型的差別只在於變數的值本身不同而已
實質類型變數的值是值本身,而參考型別的變數的值是位址的值
若是要以傳址方式傳遞參數,必須在定義方法參數時加上ref或者是out 關鍵字,
這時候在方法內執行的變數就是外部的變數本身。
小小心得
若有謬誤
請以下留言更正指教
參考https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-parameters
留言
張貼留言