不學 JAVA 換學 C# 之覺得心累 - L1:ch8 方法的進階概念
前言
參數使我們能夠在方法之間傳遞資料。C# 提供了多種參數類型,例如值參數、參考參數、輸出參數以及參數陣列。
- 值參數:
- 預設行為。
- 方法會獲得值的複製。如果是值類型 (Value types),會複製變數的值。如果是參考類型 (Reference types),會複製記憶體位址。關於變數類型請參考資料型態。
- 參考參數:
- 以
ref修飾註明。 - 是取別名,重新指派會影響原始變數的資料。
- 呼叫前需要初始化變數。
- 方法中,不一定要修改資料。
- 以
- 輸出參數:
- 以
out修飾註明。 - 用於從方法中傳回資料,在呼叫前不需初始化變數。
- 在方法中,一定要指派資料。
- 以
- 參數陣列:
- 以
params修飾註明。 - 允許傳入不固定數量的參數。
- 以
值參數 (Call/Pass by Value)
請先參考值 (value)、指標 (pointer/address)、參考 (reference)。
值類型 (Value types)
會複製變數的值。
1 | int x = 5, y = 10; |
執行 Swap 方法 line 6 ~ 8 前。
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x30 | 5 | x |
| 0x40 | 10 | y |
| 0x938 | 5 | a |
| 0x948 | 10 | b |
執行完 Swap 方法後。
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x30 | 5 | x |
| 0x40 | 10 | y |
| 0x938 | 10 | a |
| 0x948 | 5 | b |
參考類型 (Reference types)
會複製記憶體位址。
1 | void ModifyArray(int[] numbers) |
執行 line 9 後,產生變數 arr 並創造記憶體位址。
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x30 | 0x748 | arr |
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x748 | 10 | arr[0] |
| 0x758 | 20 | arr[1] |
| 0x768 | 30 | arr[2] |
| 0x778 | 40 | arr[3] |
執行 line 10,複製 arr 的值 0x748 給 numbers 並傳入 ModifyArray 方法中,因為 arr 和 numbers 都指向同一個記憶體位址,所以 arr[0] 也就是 numbers[0],以此類推,並且每個元素乘以 2。
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x30 | 0x748 | arr |
| 0x40 | 0x748 | numbers |
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x748 | 20 | arr[0] / numbers[0] |
| 0x758 | 40 | arr[1] / numbers[1] |
| 0x768 | 60 | arr[2] / numbers[2] |
| 0x778 | 80 | arr[3] / numbers[3] |
如果我在 ModifyArray 中重新指派陣列,就不會影響到原始陣列。
1 | void ModifyArray(int[] numbers) |
因為執行 line 3 時,將 numbers 的值從 0x748 換成 0x913,指向不同的陣列。
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x30 | 0x748 | arr |
| 0x40 | 0x913 | numbers |
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x748 | 10 | arr[0] |
| 0x758 | 20 | arr[1] |
| 0x768 | 30 | arr[2] |
| 0x778 | 40 | arr[3] |
| 記憶體位址 | 值 | 變數名稱 |
|---|---|---|
| 0x913 | 6 | numbers[0] |
| 0x923 | 6 | numbers[1] |
| 0x933 | 6 | numbers[2] |
| 0x943 | 6 | numbers[3] |
參考參數、輸出參數 (Call/Pass by Reference)
ref 參數修飾詞
若想要在呼叫方法中修改原始資料,可以使用參考參數 ref 。變數在呼叫前必須被初始化。
1 | using System; |
out 參數修飾詞
如果變數會在方法內被設定,使用輸出參數 out。
變數在呼叫前不必初始化,但一定要在方法內被設定,否則拋出以下錯誤。
error CS0177: The out parameter `b’ must be assigned to before control leaves the current method
1 | using System; |
變數在呼叫前可以被初始化,但是會被忽略,傳不進方法中。
1 | using System; |
即使 x 在呼叫 OutValue 前被初始化成 500,但是 500 無法傳給 b,因此 line 9 會產生以下錯誤:
error CS0269: Use of unassigned out parameter `b’
使用 ref 回傳參考值
- 在方法的回傳型態是
ref 型態,例:ref int,型態跟我想回傳參考的該變數一致。 return前加上ref。- 要接收回傳結果的變數的宣告型態前加上
ref。
限制:C# 覺得安全的參考才能回傳
主要是為了避免返回無效的參考,並確保程式執行的安全性與穩定性。
- 可以被回傳
- 被傳進方法的變數的參考。
- 不可以被回傳
- 方法內部定義的臨時變數 (局部變數) 的參考。
- 方法內部產生的值 (例如:計算結果的臨時變數) 的參考。
例子一
以下的例子中,FindRefOfValue 方法是用來找到陣列中,值等於 target 的參考 (Reference),並且回傳,讓某變數的參考等於該值,該變數就會是 target 的別名了。
在 line 21 ref FindRefOfValue(nums, 250) 去找陣列中值等於 250 的參考,也就是 nums[1] 的參考,並且回傳參考,指派給 result 的參考,result 即是 nums[1] 的別名。
1 | using System; |
例子二
在 line 19 ref UpdateBViaRef(ref b) 中會回傳 b 的參考,指派給 c 的參考,c 即是 b 的別名。
1 | using System; |
多載 (Overloading)
不學 JAVA 換學 C# 之覺得心累 - L1:ch7 方法裡面有提到:具有相同名稱但參數型態、個數與順序不同的方法會被視為不同的方法,這被稱為多載 (Overloading)。
以下的例子中,line 1 和 lin 5 的方法是型態不同, lin 5 和 line 9 的方法是個數不同。
1 | void Print(int number) { |
參數陣列
了解多載後就知道,如果我有一個方法,想要接收 2、5 或 10 個的參數,我就需要定義三個方法,第一個方法的參數列表有兩個,第二個方法的參數列表有五個,第三個方法的參數列表有十個,這樣太麻煩了,萬一要更多不固定數量的參數怎麼辦?因此就有了參數陣列。
使用 params 修飾詞來定義方法的參數,允許傳入不定數量的參數,這在需要處理多個不固定數量的參數時特別有用。
- 只能有一個
params參數,且必須是參數列表中的最後一個參數。 - 傳遞給
params參數的數值會被視為一個陣列。
1 | using System; |
參考資料:
方法參數和修飾符號
Difference between Ref and Out keywords in C#