🔧 方法與函式
📌 什麼是方法?
方法(Method)就像一台自動販賣機:
- 你投入硬幣(參數)
- 機器處理(方法本體)
- 吐出飲料(回傳值)
📌 方法宣告
// 方法的基本語法
// 存取修飾詞 回傳型別 方法名稱(參數列表)
// {
// 方法本體
// return 回傳值;
// }
// 有回傳值的方法
static int Add(int a, int b) // 接收兩個整數,回傳一個整數
{
return a + b; // 回傳兩數相加的結果
}
// 沒有回傳值的方法(void)
static void SayHello(string name) // 接收一個字串,不回傳任何值
{
Console.WriteLine($"你好,{name}!"); // 印出問候語
}
// 呼叫方法
int sum = Add(3, 5); // 呼叫 Add 方法,傳入 3 和 5
Console.WriteLine(sum); // 輸出:8
SayHello("小明"); // 呼叫 SayHello 方法
// 輸出:你好,小明!
📌 ref、out、params 關鍵字
// ref — 傳參考(方法內的修改會影響外面的變數)
static void Double(ref int number) // ref 表示傳入變數的參考
{
number *= 2; // 直接修改原來的變數
}
int value = 10; // 原始值是 10
Double(ref value); // 用 ref 傳入
Console.WriteLine(value); // 輸出:20(原始變數被修改了!)
// out — 輸出參數(方法必須在裡面賦值)
static void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b; // 商數(必須賦值)
remainder = a % b; // 餘數(必須賦值)
}
Divide(10, 3, out int q, out int r); // 呼叫時用 out 宣告變數
Console.WriteLine($"商:{q},餘:{r}"); // 輸出:商:3,餘:1
// 實際範例:int.TryParse 就是用 out 的好例子
if (int.TryParse("42", out int parsed)) // out 接收轉換結果
{
Console.WriteLine($"轉換成功:{parsed}"); // 輸出:轉換成功:42
}
// params — 可變數量參數(接受任意數量的參數)
static int Sum(params int[] numbers) // params 讓你傳入任意多個整數
{
int total = 0; // 總和初始值
foreach (int n in numbers) // 走訪所有傳入的數字
{
total += n; // 累加
}
return total; // 回傳總和
}
Console.WriteLine(Sum(1, 2, 3)); // 傳入 3 個參數 → 6
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // 傳入 5 個參數 → 15
Console.WriteLine(Sum()); // 傳入 0 個參數 → 0
📌 選擇性參數與具名引數
// 選擇性參數(Optional Parameters)— 有預設值的參數
static void Greet(string name, string greeting = "你好", int times = 1)
{
for (int i = 0; i < times; i++) // 重複印出 times 次
{
Console.WriteLine($"{greeting},{name}!"); // 印出問候語
}
}
Greet("小明"); // 使用所有預設值 → 你好,小明!
Greet("小明", "哈囉"); // 覆蓋 greeting → 哈囉,小明!
Greet("小明", "嗨", 3); // 覆蓋兩個預設值,印 3 次
// 具名引數(Named Arguments)— 指定參數名稱
Greet(name: "小美", times: 2); // 跳過 greeting,只設 times
Greet(times: 3, name: "小華", greeting: "早安"); // 順序可以不同
📌 方法多載(Method Overloading)
// 方法多載:同名方法,但參數不同
static int Multiply(int a, int b) // 整數版本
{
return a * b; // 兩個整數相乘
}
static double Multiply(double a, double b) // 浮點數版本
{
return a * b; // 兩個浮點數相乘
}
static int Multiply(int a, int b, int c) // 三個參數版本
{
return a * b * c; // 三個整數相乘
}
// 編譯器會根據傳入的參數自動選擇正確的版本
Console.WriteLine(Multiply(3, 4)); // 呼叫 int 版本 → 12
Console.WriteLine(Multiply(2.5, 3.0)); // 呼叫 double 版本 → 7.5
Console.WriteLine(Multiply(2, 3, 4)); // 呼叫三參數版本 → 24
📌 表達式主體方法(Expression-bodied Methods)
// 如果方法本體只有一行,可以用 => 簡化
static int Square(int x) => x * x; // 回傳 x 的平方
static bool IsEven(int n) => n % 2 == 0; // 判斷是否為偶數
static string GetGreeting(string name) => $"Hello, {name}!"; // 回傳問候語
static void PrintLine() => Console.WriteLine("---"); // 印出分隔線
// 使用
Console.WriteLine(Square(5)); // 輸出:25
Console.WriteLine(IsEven(4)); // 輸出:True
Console.WriteLine(GetGreeting("小明")); // 輸出:Hello, 小明!
📌 區域函式(Local Functions)
// 區域函式:在方法裡面定義的方法
static int Factorial(int n) // 計算階乘(n!)
{
// 驗證輸入
if (n < 0) throw new ArgumentException("n 不能是負數"); // 檢查參數
// 區域函式:只有 Factorial 方法內部能使用
int Calculate(int num) // 遞迴計算階乘
{
if (num <= 1) return 1; // 基底條件:0! = 1! = 1
return num * Calculate(num - 1); // 遞迴:n! = n * (n-1)!
}
return Calculate(n); // 呼叫區域函式
}
Console.WriteLine(Factorial(5)); // 5! = 5*4*3*2*1 = 120
// 區域函式的好處:
// 1. 把輔助邏輯封裝在使用它的方法裡
// 2. 可以存取外層方法的變數
// 3. 比 private 方法更能表達「這只是內部使用」
static void ProcessData(int[] data) // 處理資料的方法
{
int threshold = 50; // 門檻值
// 區域函式可以存取外層的 threshold 變數
bool IsAboveThreshold(int value) => value > threshold; // 判斷是否超過門檻
foreach (var item in data) // 走訪每個資料
{
if (IsAboveThreshold(item)) // 使用區域函式判斷
{
Console.WriteLine($"{item} 超過門檻 {threshold}"); // 印出結果
}
}
}
🤔 我這樣寫為什麼會錯?
❌ 錯誤:忘記 return
// ❌ 錯誤:宣告了回傳型別但沒有 return
static int GetMax(int a, int b)
{
if (a > b)
{
return a; // 有 return
}
// 如果 a <= b 呢?沒有 return!→ 編譯錯誤
}
// ✅ 正確:所有分支都要有 return
static int GetMaxFixed(int a, int b)
{
if (a > b)
{
return a; // a 較大時回傳 a
}
return b; // 否則回傳 b
}
❌ 錯誤:params 不是放在最後
// ❌ 錯誤:params 必須是最後一個參數
// static void Test(params int[] numbers, string name) { }
// ✅ 正確:params 放最後
static void Test(string name, params int[] numbers) // params 在最後
{
Console.WriteLine($"{name}: {numbers.Length} 個數字");
}
❌ 錯誤:out 參數沒有賦值
// ❌ 錯誤:out 參數必須在方法內賦值
// static void GetValue(out int x)
// {
// // 忘記給 x 賦值 → 編譯錯誤
// }
// ✅ 正確:一定要給 out 參數賦值
static void GetValue(out int x)
{
x = 42; // out 參數一定要賦值
}