static,屬於類別,僅能存取靜態成員。public, protected, internal, protected internal, private。| 模式 | 關鍵字 | 行為 | 呼叫端需初始化 |
|---|---|---|---|
| 傳值 | (無) | 複製值 (實值型別) 或複製參考 (參考型別) | 是 |
| 傳址 | ref | 傳遞變數位址,方法內修改影響原變數 | 是 |
| 輸出 | out | 類似 ref,但呼叫端不需初始化;方法內必須賦值 | 否 |
| 參數陣列 | params | 將可變數量引數封裝為陣列,僅能用於最後一個參數 | 視情況 |
static void SwapRef(ref int x, ref int y) { int t=x; x=y; y=t; }
static void SwapVal(int x, int y) { int t=x; x=y; y=t; }
static void Main()
{
int i=1, j=2;
SwapRef(ref i, ref j); Console.WriteLine($"Ref: i={i}, j={j}"); // 2, 1
SwapVal(i, j); Console.WriteLine($"Val: i={i}, j={j}"); // 2, 1 (未改變)
}
class Element { public int Number = 10; }
static void ChangeField(Element e) { e.Number = 100; } // 修改欄位 -> 影響原物件
static void ChangeRef(Element e) { e = new Element { Number = 100 }; } // 修改參考 -> 不影響原變數
static void Main()
{
var obj = new Element();
ChangeField(obj); Console.WriteLine(obj.Number); // 100
ChangeRef(obj); Console.WriteLine(obj.Number); // 100 (未變)
}
結論:傳值傳遞的是參考的副本,可修改物件內部狀態,無法替換呼叫端的變數指向。
out 多重回傳值範例
static void SplitName(string full, out string first, out string last)
{
var parts = full.Split(' ');
first = parts[0];
last = parts.Length > 1 ? parts[1] : "";
}
static void Main()
{
SplitName("Cornfield Lee", out var f, out var l);
Console.WriteLine($"{f} | {l}"); // Cornfield | Lee
}
params 參數陣列範例
static int Sum(params int[] nums)
{
int s = 0; foreach (var n in nums) s += n; return s;
}
static void Main()
{
Console.WriteLine(Sum(1,2,3,4,5)); // 15
Console.WriteLine(Sum(new int[]{10,20})); // 30
}
ref/out/params 差異)。| 關鍵字 | 用途 | 限制 |
|---|---|---|
virtual | 基底類別宣告可被覆寫的方法 | 不可 static、private |
override | 衍生類別提供新實作 | 必須覆寫 virtual/abstract/override 方法,簽章完全一致 |
sealed override | 封鎖繼承鏈進一步覆寫 | 僅能用於 override 方法 |
abstract | 介面式宣告,無實作,強制衍生類別實作 | 所屬類別必須 abstract,不可 static/private |
new | 隱藏基底成員 (非多型) | 靜態繫結,base.Method() 仍可呼叫被隱藏版本 |
class Parent
{
public void F() => Console.WriteLine("Parent.F"); // 非虛擬
public virtual void G() => Console.WriteLine("Parent.G");
}
class Child : Parent
{
public new void F() => Console.WriteLine("Child.F"); // 隱藏
public override void G() => Console.WriteLine("Child.G"); // 覆寫
}
static void Main()
{
Child c = new Child();
Parent p = c;
p.F(); c.F(); // Parent.F / Child.F (靜態繫結看變數型別)
p.G(); c.G(); // Child.G / Child.G (動態繫結看物件型別)
}
sealed override 防止進一步覆寫
class A { public virtual void M() => Console.WriteLine("A"); }
class B : A { public sealed override void M() => Console.WriteLine("B"); }
class C : B { /* public override void M() => ... */ } // 編譯錯誤:無法覆寫 sealed 方法
abstract class Base
{
public abstract void DoWork(); // 無實作
public virtual void Optional() => Console.WriteLine("Base.Optional");
}
class Derived : Base
{
public override void DoWork() => Console.WriteLine("Derived.Work");
// Optional 可選擇覆寫或保留基底實作
}
abstract override),強制下一層重新實作。extern) 與 P/Invoke呼叫非 C# 實作的函式 (如 Win32 API)。需配合 DllImport 屬性指定 DLL 與封送規則。
using System.Runtime.InteropServices;
class Win32
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int type);
}
class Program
{
static void Main() => Win32.MessageBox(IntPtr.Zero, "Hello from C#!", "P/Invoke", 0);
}
執行後彈出原生訊息方塊。extern 方法不可為 abstract。
精通參數傳遞語意 (ref/out/params)、多型機制 (virtual/override/sealed/abstract/new) 與外部互操作,是撰寫高效、可維護、可擴充 C# 程式碼的關鍵。