【C#】ref與out傳值與傳址

傳值Call by Value 與 傳址 Call by Reference
學過c++的一定會遇到
傳值與傳址
一開始聽到整個
wtf 這兩個是尛阿
到最近看了一下ref跟out之後才比較了解

一般資料型別分為兩種
一個是實質型別
一個是參考型別

測試看看

實質型別就是 int float char…等基礎的型別
而他變數記憶體存放的內容是”“所以稱為實”“型別
也就是”傳值“,當你在使用他的時候
他們之間傳遞的方式都是把值copy一份後再傳進去
像是以下

1
2
3
4
5
6
7
int A=5, B=10;

void ChangeValue(int a,int b)
{
a = 1;
b = 2;
}

之後在Start方法內調用

1
2
3
Debug.Log(" A  :" + A + " b :" + B);
ChangeValue(A, B);
Debug.Log(" A :" + A + " b :" + B);

下圖分別是將AB傳進去前跟傳進去後的結果

可以看到傳進去後值都沒改變,這是因為使用的方式只是傳值(call by value)而已
意思就是把“值”複製給對方,而複製完改變的東西並不會影響到本身的值,因此不會做任何改變

而下面對照

1
2
3
4
5
6
7
Car car11 = new Car("car11");
Car car22 = new Car("car22");
void ChangeNewCarValue(Car _car11, Car _car22)
{
_car11._carName = "ChangeCAR11";
_car22._carName = "ChangeCAR22";
}

同樣的宣告 同樣的方法 同樣在Start內打上

1
2
3
Debug.Log(" car11 :" + car11._carName + " car22 :" + car22._carName);
ChangeNewCarValue(car11, car22);
Debug.Log(" car11 :" + car11._carName + " car22 :" + car22._carName);

出來的結果則為

結果卻改變了,因此從上可以推論出來Car是一個傳址Call by Reference
意思就是,它傳遞的不是複製一份出來的值,而是傳遞丟入物件的記憶體位址
然後間接透過它來操作物件,跟指標很類似。

其實在C/C++的時候 正確是有三個名詞的
call by value 傳值、call by reference 傳參考、call by address傳址
但由於這裡要講的是C#,所以用官方的翻譯來敘述 ->

如果要詳細了解這三個有什麼差別 可以再去google

ref、out

ref 關鍵字

會導致引數由參考加以傳遞,而非透過值。
參考程式碼 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RefExample
{
static void Method(ref int i)
{
// Rest the mouse pointer over i to verify that it is an int.
// The following statement would cause a compiler error if i
// were boxed as an object.
i = i + 44;
}

static void Main()
{
int val = 1;
Method(ref val);
Console.WriteLine(val);
// Output: 45
}
}

可以發現實質類型使用ref關鍵字之後,就會變得像是上面參考類型一樣
可以修改傳入的參數了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class RefExample
{
static void Method(ref int i)
{
// Rest the mouse pointer over i to verify that it is an int.
// The following statement would cause a compiler error if i
// were boxed as an object.
i = i + 44;
}

static void Main()
{
int val = 1;
Method(ref val);
Console.WriteLine(val);

// Output: 45
}
}

那問題來了,參考類型本身傳遞就可以直接修改值了
為何還會使用ref關鍵字加到參考型別身上呢?

  1. 明確提示
    加了之後 不管給別人用還是自己用 都可以知道 傳入的參數是會被改變值得
    不管傳入實質或參考都一樣
  2. string的不變性 參考連結
  3. 為了修改默認值類型

- ref重點:

  1. 傳遞至 ref 參數的引數,在傳遞之前必須先初始化。
  2. 不能將ref和out用於async修飾詞定義的非同步方法。
  3. 有進有出

out 關鍵字

基本上跟ref一樣
想要多個回傳值的時候,可以使用out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class OutReturnExample
{
static void Method(out int i, out string s1, out string s2)
{
i = 44;
s1 = "I've been returned";
s2 = null;
}
static void Main()
{
int value;
string str1, str2;
Method(out value, out str1, out str2);
// value is now 44
// str1 is now "I've been returned"
// str2 is (still) null;
}
}

ref跟out在使用上有很微小的差距

使用out的話,他不需要在被調用前初始化
但是調用者需要在返回之前指定輸出的參數

另一方面,可以用這樣去想
out類似於將方法附加返回值,也就是回傳多個值