| 維度 | C# (CLR) | C++ (標準) |
|---|---|---|
| 語法關鍵字 | `static ClassName() { }` | 無原生支援,需手動實現或模板模擬 |
| 觸發時機 | 首次訪問靜態成員 **或** 創建首個實例前,**保證在此之前** | 無標準保證,全局靜態對象初始化順序跨編譯單元**未定義** |
| 線程安全 | **CLR 保證**:進程內僅執行一次,內部含記憶體柵欄 | C++11 起函數局部靜態變量初始化線程安全,**但全局/類靜態成員無保證** |
| 參數/訪問修飾符 | 無參數、無修飾符、不可直接調用 | 模擬實現可自由設計 |
| 靜態方法觸發 | **支援**:調用靜態方法前自動觸發 | 模擬實現通常**不支援**(需顯式調用) |
| 異常處理 | 異常導致類型不可用,後續訪問拋 `TypeInitializationException` | 模擬實現需自行處理 |
public class Database {
// 靜態構造函數:無修飾符、無參數
static Database() {
Console.WriteLine("靜態構造函數執行");
ConnectionString = LoadConfig();
InitializePool();
}
public static string ConnectionString { get; private set; }
public static void Execute(string sql) { /* ... */ }
public Database() { Console.WriteLine("實例構造函數"); }
}
// 測試
Database.Execute("SELECT 1"); // 1. 靜態構造 2. Execute
var db = new Database(); // 3. 實例構造(靜態構造已完成,不再執行)
C++ 標準**無靜態構造函數概念**。全局/命名空間/類靜態成員初始化順序在**不同編譯單元(.cpp)間未定義**,著名的「靜態初始化順序災難」。
// File1.cpp
Logger g_logger; // 全局對象
// File2.cpp
Config g_config; // 另一全局對象
// File3.cpp
Database g_db(g_config.GetConnectionString()); // 依賴 g_config
// 若 g_db 先於 g_config 初始化 → 讀取未初始化配置 → 崩潰/UB
Config& GetConfig() {
static Config instance; // 首次調用初始化,線程安全
return instance;
}
Database& GetDatabase() {
static Database instance(GetConfig()); // 依賴順序自然解決
return instance;
}
template
class static_constructable {
protected:
static_constructable() {
static helper placeholder; // 函數局部靜態,首次構造派生類時初始化
}
private:
struct helper { helper() { T::static_constructor(); } };
};
// 使用
class A : static_constructable {
public:
static void static_constructor() { /* 初始化邏輯 */ }
A() { /* 實例構造 */ }
};
// 單例模式:線程安全、延遲初始化、無靜態順序問題
class Database {
Database() { LoadConfig(); InitPool(); }
public:
static Database& Instance() {
static Database instance; // C++11 魔法
return instance;
}
void Execute(string sql) { /* ... */ }
};
// 使用
Database::Instance().Execute("SELECT 1");
這就是 C++ 的「靜態構造函數」:**按需初始化、線程安全、無順序災難**。
| 場景 | C# | C++ |
|---|---|---|
| 複雜靜態初始化邏輯 | 直接寫 `static ClassName()`,CLR 全管 | 改用函數局部靜態單例/工廠,拒絕全局靜態對象 |
| 靜態方法需觸發初始化 | 原生支援 | 靜態方法內調用 `Instance()` 確保初始化 |
| 跨 DLL 初始化順序 | CLR 管理,無憂慮 | 極度危險,**嚴禁**跨 DLL 依賴全局靜態對象 |
| 異常安全 | 失敗即類型失效,自動包裝異常 | 需自行 `try/catch` 在工廠函數中 |
C# 靜態構造函數是 **語言+運行時** 共同保證的強語義;C++ 則通過 **語言機制(函數局部靜態)+ 設計模式** 達到同等甚至更可控的效果。跨語言開發時,**不要試圖在 C++ 中完美複製 C# 語義**,而應擁抱各自語言的最佳實踐。