不學 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#