限制程式局部區塊僅於特定.NET環境可用

隨.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可使用而其餘平台無法使用該屬性。