#if/#endif 易被濫用,導致程式碼難讀、難除錯、不同建構配置行為不一致。C# 提供條件屬性([Conditional("SYMBOL")]),針對方法啟用/停用呼叫,結構清晰、編譯器最佳化、避免常見陷阱。
// 錯誤:DEBUG 時 msg 賦值,RELEASE 時 msg 為 null → 輸出空行
string msg = null;
#if DEBUG
msg = GetDiagnostics();
#endif
Console.WriteLine(msg); // RELEASE 輸出空行,非預期行為
散落的 #if 讓邏輯支離破碎,除錯時需心理模擬不同配置下的程式碼。
[Conditional("DEBUG")]
private void CheckState()
{
var method = new StackTrace().GetFrame(1).GetMethod().Name;
Debug.Assert(_lastName != null, method, "Last Name cannot be null");
Debug.Assert(_lastName.Length > 0, method, "Last Name cannot be blank");
Trace.WriteLine($"CheckState for Person: called by {method}");
}
// 呼叫端:
public string LastName
{
get { CheckState(); return _lastName; }
set { CheckState(); _lastName = value; CheckState(); }
}
CheckState() 呼叫,方法本體編譯進組件。相當於:
// DEBUG 定義時
get { CheckState(); return _lastName; }
// DEBUG 未定義時
get { return _lastName; }
[Conditional] 屬性並列。[Conditional("DEBUG"), Conditional("TRACE")] → 任一定義即啟用。
#if DEBUG && TRACE
#define BOTH
#endif
[Conditional("BOTH")]
void Method() { ... }
Debug.Assert、Trace.WriteLine 皆以此機制實作。| 面向 | #if/#endif | Conditional Attribute |
|---|---|---|
| 程式碼結構 | 碎片化、巢狀難讀 | 方法層級、結構清晰 |
| 編譯器檢查 | 無法檢查邏輯一致性 | 強制 void 回傳、無副作用 |
| IL 體積 | 殘留死碼 | 移除呼叫指令,方法本體保留但不 JIT |
| 除錯體驗 | 需心理模擬多配置 | 原始碼單一版本,設定符號即可 |