1. 前言
每個寫 code 的人都知道:函式(或叫方法)基本上就是任何應用程式的「工作馬」。它們幫我們把程式碼結構化、重複利用邏輯,還能避免一直 copy paste 到天荒地老。到目前為止,你遇到的所有函式,都是 class 或 struct 裡的「鄰居」。
但想像一下,如果你有一個很大的函式,裡面有一小段邏輯你想抽出來,但又不想讓整個專案都看到它。比如說,一個只在這個方法裡才需要的小計算。
這時候,區域函式 就派上用場啦。
區域函式 就是宣告在另一個函式(方法、建構子、屬性,甚至另一個區域函式)裡的函式。這種區域函式只會被「老爸」看到,其他地方都看不到。
區域函式語法
void MainMethod()
{
Console.WriteLine("開始執行主方法。"); // 開始執行主方法。
int result = InnerSum(5, 7); // 呼叫區域函式 InnerSum(內部加總)
Console.WriteLine($"區域函式結果: {result}"); // 區域函式結果: {result}
// 區域函式直接在 MainMethod 裡宣告
int InnerSum(int a, int b) // 區域函式:兩個數字相加 { return a + b; // 回傳 a 跟 b 的加總 }
}
在這個例子裡,InnerSum 這個函式是直接寫在 MainMethod 裡的。它在主方法外面完全看不到——就像一個只有你自己知道的小祕密指令。
2. 怎麼宣告、什麼時候用區域函式
基本規則
- 區域函式可以宣告在:方法、建構子、屬性、運算子,甚至另一個區域函式裡!
- 區域函式只在自己的作用域(scope)裡看得到。
- 你可以在父函式裡,宣告前或宣告後呼叫區域函式(C# 會自動幫你分析)。
範例:計算平均分數
// 主要函式:印出平均分數
void PrintAverageScore(int[] scores)
{
if (scores.Length == 0)
{
Console.WriteLine("沒有分數可以計算。"); // 沒有分數可以計算。
return;
}
double average = CalculateAverage(scores);
Console.WriteLine($"平均分數: {average:0.00}"); // 平均分數
// 區域函式:計算平均值
double CalculateAverage(int[] arr) { int sum = 0; foreach (var score in arr) sum += score; return (double)sum / arr.Length; // 回傳平均值 }
}
// 呼叫函式:
int[] myScores = { 5, 4, 3, 4, 5 };
PrintAverageScore(myScores); // 會印出:平均分數: 4.20
有沒有很方便?PrintAverageScore 外面的人完全不能用它裡面的區域函式 CalculateAverage。這種寫法可以讓你的 code 很整齊,也不會把一堆輔助方法塞到全域命名空間裡。
3. 區域函式跟作用域(scope)
區域函式最大的特色就是——它的作用域只在「老爸」方法裡。這樣可以避免 class 裡一堆只用一次的小方法亂飛。
+-----------------+
| 類別方法 |
| void Foo() |
| |
| +-----------+ |
| | 區域函式 | |
| +-----------+ |
+-----------------+
區域函式只會存在於宣告它的方法裡。你如果想在外面呼叫它,就像打電話給一條不存在的祕密專線一樣,完全沒門。
如果你不小心在父方法外面呼叫區域函式,會直接編譯錯誤:名稱 '...' 在目前的內容不存在。
區域函式可以看到父方法的變數
區域函式可以存取父方法裡的所有變數——這就是它超強的地方!
void ShowStudent(string name, int age)
{
string message = BuildMessage();
Console.WriteLine(message);
// 區域函式用到 "name" 跟 "age" 變數
string BuildMessage() { return $"名字: {
name}, 年齡: {
age}"; }
}
這樣寫不只更短,也不會讓 code 很吵。你不用每次都傳一堆參數進去——區域函式直接「看」到父方法的所有東西。
4. 區域函式可以寫在迴圈或條件裡
區域函式可以寫在任何巢狀層級——甚至在迴圈或 if 裡面:
void ProcessNumbers(int[] numbers)
{
foreach (int number in numbers)
{
if (number % 2 == 0)
{
Console.WriteLine($"{number} — 偶數");
PrintEvenMessage();
}
else
{
Console.WriteLine($"{number} — 奇數");
}
void PrintEvenMessage() { Console.WriteLine("這是偶數,恭喜!"); }
}
}
不過這種寫法如果區域函式太多或太長,code 會很難讀。不要亂用,但知道可以這樣做還是很有幫助。
區域函式 vs 一般 private 方法
| 比較項目 | 區域函式 | 類別 private 方法 |
|---|---|---|
| 作用域 | 只有父函式 | 整個類別 |
| 能不能存取父變數 | 可以 | 不行(只能靠參數) |
| 易讀性 | 很高 | 普通 |
| 重複利用潛力 | 低 | 高 |
| 好不好測試 | 不好測 | 很好測 |
5. 常見錯誤跟實作細節
錯誤一:拼錯或忘記宣告區域函式。
如果你呼叫區域函式時名字打錯或根本沒宣告,編譯器會報 CS0103 或 CS0128,然後 code 直接不能跑。
錯誤二:改到被捕捉的變數(closure)。
如果區域函式去改父方法的變數,結果可能跟你想的不一樣——被捕捉的變數有時像複製,有時像參考,改了會影響邏輯。
錯誤三:忽略 IDE 建議把 private 方法變成區域函式。
如果你在 class 裡留一堆沒必要的 private 方法,IDE(像 Rider)建議你改成區域函式卻不理它,code 很快就會變亂,越來越難讀。
錯誤四:區域函式巢狀太深。
巢狀太多層的函式會讓人看不懂、debug 也很痛苦。簡潔才是王道。
GO TO FULL VERSION