核心差異速查表
| 特性 | 靜態方法 | 非靜態方法 |
| 所屬 | 類別 | 實例 |
| 呼叫語法 | ClassName.Method() | instance.Method() |
| this 參考 | 無 (編譯錯誤) | 指向呼叫實例 |
| 存取靜態成員 | 直接存取 | 直接存取 |
| 存取非靜態成員 | 需透過實例參考 | 直接存取 (隱含 this) |
| 可為 virtual/override | 不可 | 可 |
| 可為 abstract | 不可 | 可 |
為什麼靜態方法不能存取非靜態成員?
非靜態成員存在於每個實例獨立的記憶體位置。靜態方法無 this 參考,編譯器無法知道要操作哪個實例的欄位。強行允許會導致邏輯模糊。
解決方案:傳遞實例參考
public class Window {
public string Title { get; set; }
public bool IsActive { get; private set; }
public IntPtr Handle { get; private set; }
// 靜態欄位:追蹤總視窗數
private static int _totalWindows = 0;
public static int TotalWindows => _totalWindows;
public Window(string title) {
Title = title;
_totalWindows++;
Handle = CreateNativeWindow();
}
~Window() {
_totalWindows--;
DestroyNativeWindow(Handle);
}
// 非靜態方法:操作特定視窗
public void Activate() => IsActive = true;
// 靜態方法:透過參數存取實例成員
public static string GetWindowTitle(Window window) {
if (window == null) throw new ArgumentNullException(nameof(window));
return window.Title; // 經由參數存取非靜態屬性
}
// 靜態工廠方法:建立並追蹤實例
public static Window Create(string title) => new Window(title);
}
實際呼叫範例
var w1 = Window.Create("Main Window");
var w2 = Window.Create("Settings");
Console.WriteLine($"Total windows: {Window.TotalWindows}"); // 2
Console.WriteLine(Window.GetWindowTitle(w1)); // "Main Window"
w1.Activate(); // 非靜態呼叫
// Window.Activate(); // 編譯錯誤:需要實例
靜態建構子 vs 靜態方法
static ClassName():類別初始化器,自動在首次存取前執行一次,無參數、無存取修飾詞。
- 靜態方法:可隨時呼叫,支援參數、多載、存取修飾詞。
常見設計模式應用
| 模式 | 靜態方法角色 | 非靜態方法角色 |
| 單例 | Instance 屬性 getter、Create() 工廠 | 業務邏輯方法 |
| 工廠方法 | CreateX() 靜態工廠 | 產品介面實作 |
| 策略 | 無 | 策略介面實作 (非靜態) |
| 擴充方法 | 靜態類別中的靜態方法 (this 參數) | 無 |
效能微觀差異
- 靜態方法呼叫:
call IL 指令,直接跳轉,極快。
- 非靜態方法呼叫:
callvirt IL 指令,包含 null 檢查與虛擬分派查找 (即使非 virtual 也用 callvirt 保護 null)。
- 現代 JIT 對非虛擬非靜態方法會內聯優化,差異可忽略。
最佳實務
- 無狀態、純計算、輔助功能 → 靜態方法。
- 操作實例狀態、需多態、實作介面 → 非靜態方法。
- 避免「上帝類別」累積大量靜態方法,改用靜態工具類別 (如
Math, Enumerable)。