作為IT邦的第一篇文章
就來開這個主題好了
public class ValueModel
{
public int Value { get; set; }
}
public static void Assign(ValueModel model)
{
model.Value = 10;
}
public static void Assign(int value)
{
value = 10;
}
static void Main(string[] args)
{
var model = new ValueModel();
Assign(model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 10
int value = 0;
Assign(value);
Console.WriteLine($"Value is {value}");//Value is 0
}
有點基本功的都能夠知道如果是用class傳入function,在function內修改的話,呼叫端的class也會一併修改
而 ValueType的 int則不會
所以如果希望int也有像reference的效果,加個ref就可以 如下
public static void Assign(ref int value)
{
value = 10;
}
static void Main(string[] args)
{
int value = 0;
Assign(ref value);
Console.WriteLine($"Value is {value}");//Value is 10
}
那今天的問題來了 如果ref用在ReferenceType上會怎樣?
public static void Assign(ref ValueModel model)
{
model.Value = 20;
}
static void Main(string[] args)
{
{
var model = new ValueModel();
Assign(model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 10
}
{
var model = new ValueModel();
Assign(ref model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 20
}
}
恩 看起來好像效果一樣? 似乎沒差別
在這個例子裏面是這樣沒錯,因為我們沒有動到關鍵點 我們修改一下Assign的內容
public static void Assign(ValueModel model)
{
model = new ValueModel();
model.Value = 10;
}
public static void Assign(ref ValueModel model)
{
model = new ValueModel();
model.Value = 20;
}
static void Main(string[] args)
{
{
var model = new ValueModel();
Assign(model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 0
}
{
var model = new ValueModel();
Assign(ref model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 20
}
}
這樣應該可以很容易地看出差異了
想像一下
若是沒有加上ref的時候 其實方法呼叫時會將傳入的物件貼上一個標籤
修改資料是對這個標籤上的物件做修改 而model = new ValueModel(); 其實則是將標籤貼到new出來的物件上
所以去修改的時候 是修改新new出來的物件 而不是呼叫端傳入的
那加上ref呢?
他是將外面呼叫端的標籤傳入 所以model = new ValueModel();是將外面的標籤給貼到裡面new出來的物件上
一開始呼叫端new的物件早就消失不見了
(沒有任何方法可以存取他 因為它唯一的標籤已經被撕掉 然後貼到方法內的物件上了)
我們可以再做個實驗
static void Main(string[] args)
{
{
var model = new ValueModel();
var oldmodel = model;
Assign(ref model);
Console.WriteLine($"ValueModel.Value is {model.Value}");//ValueModel.Value is 20
Console.WriteLine($"oldmodel.Value is {oldmodel.Value}");//oldmodel.Value is 0
}
}
在呼叫端的model被撕掉標籤以前 我先給它貼上一個oldModel的標籤
就可以清楚看見oldmodel其實根本沒有被修改值
其實也可以用GetHashCode來檢驗是否為同一物件
好了~ 今天分享就此結束!
本人並非本科系出身,也沒有受過什麼專業訓練
都是自己在寫Code過程中的感觸體會
如果一些專業術語使用不當或是觀念不正確,還請海涵指教
嘗試用記憶體角度來解釋
var model = new ValueModel();
這一行會創造兩個記憶體空間
所以可以通過 model 這個變數來找到 ValueModel
今天如果是普通的呼叫
Assign(model);
public static void Assign(ValueModel _model)
{
...
}
程式會另外創造一個記憶體空間 _model,起始記憶體地址假設為0003,內容為 model 的內容,也就是記憶體地址0001
所以在 Method 中操作 _model 會影響到 ValueModel
但如果是創造另一個 ValueModel 賦值給 _model
_model = new ValueModel();
程式會另外創造一個記憶體空間 ValueModel,起始記憶體地址假設為0004
然後將 _model 的內容從 記憶體地址0001 更改成 記憶體地址0004
這樣 _model 就跟一開始的 ValueModel 沒有任何關係了
但是外面的變數 model 的內容還是記憶體地址 0001
======================================================
今天如果是 Ref 的呼叫
Assign(ref model);
public static void Assign(ref ValueModel _model)
{
...
}
程式並不會幫 _model 另外創造記憶體空間,直接將 _model 等於 model
換句話說就是 _model 本身的記憶體地址就是 0002
如果再執行一樣的動作
_model = new ValueModel();
程式會另外創造一個記憶體空間 ValueModel,起始記憶體地址假設為0004
然後將 _model 的內容從 記憶體地址0001 更改成 記憶體地址0004
但是 _model 等於外面的變數 model(本身的記憶體地址相同)
所以造成外面的 model 也會一起改變的現象