隨.NET各種環境的增加,在撰寫程式時部分功能在特定的環境中可或不可運作,這時候就可以利用前置處理器指示詞加以實現。
前置處理器指示詞用以達成條件式編譯的功能,在針對各平台編譯時可以透過所設的條件產生對應版本。
本篇則以一個.NET Core上建立Timer類別作為指示詞使用範例。
本範例使用.NET Core Console專案建立,並建立MyTimer類別(點我查看專案)。
前情
Thread.Abort為強制中斷執行緒的方法,但此方法並不適用於.NET Core。
一、建立符號
編輯csproj檔案後再PropertyGroup中加入DefineConstants標籤,定義一符號IsNetFramework為目標框架符合正規表示法 ^net\d+$ 以區分是否為.NET Framework,TargetFramework類型可見此文件。
如下定義,我們已經在此專案定義IsNetFramework符號,表示目前編譯環境是否為.NET Framework。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
<DefineConstants Condition="$([System.Text.RegularExpressions.Regex]::IsMatch($(TargetFramework), '^net\d+$'))">$(DefineConstants);IsNetFramework</DefineConstants>
</PropertyGroup>
</Project>
二、定義編譯條件與範圍
透過前項提及的前置處理器指示詞,我們使用 #if #else #endif 選定條件式編譯的條件與圈選範圍。
前情中已經提及在.NET Core中Thread並不支援Abort方法,但此類別必須要同時支援兩環境,我們使用前面提到的指示詞,將使用到Abort的範圍包起來,並設定編譯條件為IsNetFramework,如此此段程式碼僅會於編譯目標為.NET Framework環境時才使用。
// https://gist.github.com/XuPeiYao/35cd4e5ae653117bd9ecb9e67f524bf8#file-mytimer-cs-L60
/// <summary>
/// 啟用計時器
/// </summary>
public void Start() {
lock (lockObject) {
if (enable) return;
mre = new ManualResetEvent(true);
timerLoopTokenSource = new CancellationTokenSource();
timerLoop = Task.Factory.StartNew(() => {
if (FireTickOnEnable) Tick?.Invoke(this, mre);
while (Enable) {
Thread.Sleep(Interval);
if (timerLoopTokenSource == null) return;
#if IsNetFramework
using (timerLoopTokenSource.Token.Register(Thread.CurrentThread.Abort)) {
Tick?.Invoke(this, mre);
}
#else
Tick?.Invoke(this, mre);
#endif
mre.WaitOne();
}
}, timerLoopTokenSource.Token);
enable = true;
}
}
上面中的使用僅是程式碼的片段,除此外我們也可以直接將某個方法、屬性使用條件式編譯,如這個MyTimer類別中的HardStop屬性必須依賴Thread.Abort才可實現,所以在.NET Core則不需要實現。
#if IsNetFramework
/// <summary>
/// 取得或設定停止時強制中斷<see cref="Tick"/>
/// </summary>
public bool HardStop { get; set; }
#endif
經過上面的方法後在.NET Framework中HardStop可使用而其餘平台無法使用該屬性。