條件編譯允許在編譯時決定包含或排除代碼區塊,常見用途:
C# 無獨立預處理器,編譯器內建模擬,支援:
// 定義符號(檔案層級,必須在最前面,using 之前)
#define DEBUG
#define TRIAL_VERSION
// 取消定義
#undef TRIAL_VERSION
// 條件包含代碼
#if DEBUG
Console.WriteLine("除錯模式:啟用詳細日誌");
#elif RELEASE && !TRIAL_VERSION
Console.WriteLine("正式版:完整功能");
#else
Console.WriteLine("試用版:功能受限");
#endif
// 邏輯運算子:&& (AND)、|| (OR)、! (NOT)
#if (DEBUG || TESTING) && !DISABLE_LOGGING
Log.Verbose("進入關鍵路徑");
#endif
// 編譯時警告/錯誤
#if DEMO_VERSION && DEBUG
#warning 正在建置 Demo 版除錯建置,請確認配置
#endif
#if DEBUG && RELEASE
#error 無法同時定義 DEBUG 與 RELEASE
#endif
csc /define:DEBUG;TRACE;MY_FEATURE Program.cs
csc /define:RELEASE /define:ENTERPRISE_EDITION Program.cs
更優雅、型別安全的方式,標記方法僅在符號定義時編入:
using System.Diagnostics;
public class Logger
{
// 僅當 DEBUG 定義時,此方法與其呼叫才會被編譯
[Conditional("DEBUG")]
public static void Debug(string message)
{
Console.WriteLine($"[DEBUG] {DateTime.Now:HH:mm:ss} {message}");
}
[Conditional("TRACE")]
public static void Trace(string format, params object[] args)
{
Console.WriteLine($"[TRACE] {string.Format(format, args)}");
}
// 多重條件:任一符號定義即編入(邏輯 OR)
[Conditional("DEBUG"), Conditional("FORCE_LOG")]
public static void ForceLog(string message) { ... }
}
// 呼叫端
Logger.Debug("這行在 Release 版會完全消失,零開銷");
Logger.Trace("用戶 {0} 登入", userName);
void編譯器可產出 XML 文件檔,支援 IntelliSense、API 文件生成器(DocFX、Sandcastle)、IDE 工具提示。
csc /doc:MyLib.xml Program.cs| 標籤 | 用途 | 範例 |
|---|---|---|
<summary> | 類別/方法/屬性簡短描述 | <summary>計算兩數總和</summary> |
<remarks> | 補充說明、用法範例 | <remarks>此方法執行緒安全</remarks> |
<param name="p"> | 參數描述 | <param name="a">第一個加數</param> |
<returns> | 傳回值描述 | <returns>兩數之和</returns> |
<exception cref="T"> | 可能拋出的例外 | <exception cref="ArgumentNullException">參數為 null</exception> |
<example> | 代碼範例 | 見下方完整範例 |
<code> | 程式碼片段 | <code>var sum = Add(1,2);</code> |
<see cref="T"> | 參考其他型別/成員 | <see cref="Math.Max"/> |
<seealso cref="T"> | 參考相關主題 | <seealso cref="System.Math"/> |
<para> | 段落分隔 | 用於 remarks/example 內部 |
<list type="bullet/number/table"> | 列表/表格 | 見下方完整範例 |
<paramref name="p"/> | 參考參數名(斜體顯示) | 參數 <paramref name="a"/> 不可為負 |
<typeparam name="T"> | 泛型型別參數 | <typeparam name="T">元素型別</typeparam> |
using System;
using System.Net;
using System.IO;
using System.Text;
///
/// 從 Web 伺服器擷取網頁內容的工具類別。
///
///
/// 支援同步與非同步操作。
/// 內部使用 與 處理網路傳輸。
///
/// 建構函式: 或
/// 設定 URL: 屬性
/// 取得內容:
///
///
///
///
/// var client = new RequestWebPage("https://example.com");
/// if (client.GetContent(out string html))
/// {
/// Console.WriteLine(html.Substring(0, 200));
/// }
///
///
///
public class RequestWebPage
{
private const int BufferSize = 4096;
private string _url;
///
/// 無參數建構函式,稍後需設定 屬性。
///
public RequestWebPage() { }
///
/// 指定目標 URL 的建構函式。
///
/// 完整網址(含 https:// 或 https://)
/// 為空白
public RequestWebPage(string url)
{
Url = url ?? throw new ArgumentNullException(nameof(url));
}
///
/// 目標網頁 URL。
///
/// 完整網址字串
/// 設定為空白字串
public string Url
{
get => _url;
set => _url = value ?? throw new ArgumentNullException(nameof(value));
}
///
/// 擷取網頁內容。
///
/// 輸出參數,接收網頁 HTML 字串
/// 成功傳回 true;URL 未設定或網路錯誤傳回 false
/// URL 未設定
/// 網路連線失敗
public bool GetContent(out string content)
{
content = string.Empty;
if (string.IsNullOrEmpty(_url))
throw new ArgumentException("URL 必須指定", nameof(Url));
try
{
var request = WebRequest.Create(_url);
using var response = request.GetResponse();
using var stream = response.GetResponseStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
content = reader.ReadToEnd();
return true;
}
catch (WebException)
{
return false;
}
}
}
DEBUG;TRACE
bin\Debug\$(AssemblyName).xml
RELEASE;TRACE
bin\Release\$(AssemblyName).xml
條件編譯讓單一代碼庫支援多種建置變體,#if 適合區塊控制,[Conditional] 適合方法層級且零呼叫開銷。XML 文件註解則將文件內嵌代碼,配合工具鏈實現「文件即代碼,代碼即文件」。兩者結合,構建專業、可維護、多版本共存的企業級 .NET 代碼庫。