作者: zyl910
一、原初
.NET平臺很早就提供了條件編譯的語法(#if),
但是當時官方未制定標準的條件編譯符號(Conditional compilation symbols)的名稱,而是讓各程式自行約定,
由于早期只有“.NET Framework”這一種平臺,且每次升級都是向下兼容的,沒有標準的標準的前處理器符號名,確實能減少復雜度,
二、混亂期
而到了.NET 4.0、VS2010 的時代,除了“.NET Framework”平臺外,還多了“Silverlight”、“XBox”、“Windows Phone 7”等平臺,
不久還出現了 PCL(Portable Class Libraries,可移植庫)這樣能在多個平臺上的庫,且.NET 開始支持“WinRT/UWP”、“Android”、“iOS”等平臺,
此時條件編譯就很重要了,可利用條件編譯對各平臺做不同的處理,且有時為了避免編譯失敗或做降級處理,需要判斷平臺的版本,
VS2015開始支持共享專案(Shared Project),能在代碼視窗隨時使用下拉框來切換平臺版本,對條件編譯的需求越來越大了,
可是由于此時沒有統一的條件編譯符號名稱的約定,大家各自為政,導致代碼的可讀性、可移植性很差,
隨著開源代碼的傳播,符號名稱逐漸形成了一些共識,這一問題稍微有了好轉,
但還有一個更棘手的問題——C#里的條件編譯只能進行布爾(bool)檢查,不支持版本數值比較,導致判斷版本很費勁,
2.1 僅考慮“.NET Standard”平臺時的條件判斷
例如有一段代碼,需要在“.NET Standard 1.3”以上環境運行,最開始可以這樣寫條件編譯的判斷:
#if NETSTANDARD1_3
Console.WriteLine("Run on .NET Standard 1.3+");
#endif
注意“.NET Standard”是不斷升級的,目前最新是 2.1版,于是條件編譯的判斷需寫成這樣:
#if (NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 || NETSTANDARD2_0 || NETSTANDARD2_1)
Console.WriteLine("Run on .NET Standard 1.3+");
#endif
若“.NET Standard”以后發布了新版本,那么這個條件編譯判斷得同步修改,
2.2 同時支持“.NET Standard”、“.NET Framework”平臺
“.NET Framework”是兼容“.NET Standard”的,常用版本的對應關系是——
- .NET Standard 1.3:.NET Framework 4.6
- .NET Standard 1.4~2.0:.NET Framework 4.6.1
- .NET Standard 2.1:.NET Framework 不支持
例如在“.NET Standard 1.3”上運行的代碼,是能在1.3對應的“.NET Framework 4.6”上運行的,于是條件編譯的判斷改寫成這樣:
#if (NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 || NETSTANDARD2_0 || NETSTANDARD2_1) || (NET46)
Console.WriteLine("Run on .NET Standard 1.3+, .NET Framework 4.6+");
#endif
注意“.NET Framework”是不斷升級的,目前最新是 4.8版,于是條件編譯的判斷該寫成這樣:
#if (NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 || NETSTANDARD2_0 || NETSTANDARD2_1) || (NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48)
Console.WriteLine("Run on .NET Standard 1.3+, .NET Framework 4.6+");
#endif
若“.NET Standard”、“.NET Framework”任一發布了新版本,那么這個條件編譯判斷得同步修改,
可見,進行多平臺的版本判斷,條件編譯會寫的很冗長,而且隨著版本更新,得修改條件加新版本的符號,無法一勞永逸,
三、NET5的統一
到了NET5,官方終于制訂了標準的條件編譯符號,詳見官方檔案《SDK 樣式專案中的目標框架》中的“.NET 目標框架的前處理器符號的完整串列”: https://docs.microsoft.com/zh-cn/dotnet/standard/frameworks#how-to-specify-a-target-framework
- .NET Framework: NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20
- .NET Standard: NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0
- .NET 5 及更高版本(和 .NET Core): NET, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0
而且官方還貼心給出“_OR_GREATER”后綴的符號,用于簡化版本判斷,例如“NETSTANDARD1_3_OR_GREATER”表示“.NET Standard 1.3”或更高版本,
- .NET Framework: NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER
- .NET Standard: NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER
- .NET 5 及更高版本(和 .NET Core): NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER
有了這些標準符號后,剛才的條件編譯判斷便可以寫的很簡單了,
#if (NETSTANDARD1_3_OR_GREATER) || (NET46_OR_GREATER)
Console.WriteLine("Run on .NET Standard 1.3+, .NET Framework 4.6+");
#endif
四、使用經驗
NET5雖然好,但是有一些舊專案短期內不能升級.NET版本、不能升級VS開發環境,且有時我們需開發支持舊平臺的類別庫,
此時VS不會像NET5那樣,自動為我們提供標準的條件編譯符號,
于是我們得自立更生,手工配置好條件編譯符號,可參考NET5標準的條件編譯符號,來進行配置,這樣能便于未來的平滑升級,
“_OR_GREATER”后綴的符號雖然好用,但對于手工配置條件編譯符號來說,太麻煩了,故可以不用,
為了簡化配置條件編譯符號的配置,建議僅配置當前版本的符號,例如——
- 對于“.NET Standard 1.3”的專案,僅需配置“NETSTANDARD;NETSTANDARD1_3”,
- 對于“.NET Framework 4.6”的專案,僅需配置“NETFRAMEWORK;NET46”,
由于舊版本的版本號已確定,僅是新版本的版本號無法確定,于是可以考慮反向進行條件編譯判斷,先判斷出不兼容的舊版本,這樣便能一勞永逸,即使版本升級也有效,
“.NET Framework”的歷史很長,若將所有的舊版本都列上,那會太冗長了,考慮到.NET 4.0時代才有多平臺概念,故一般情況下,向前兼容只需做到.NET 4.0,有特殊需求時,才考慮支持更舊的版本,
根據這些經驗,剛才的條件編譯判斷,可寫成這樣:
#if (NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2) || (NET40 || NET45 || NET451 || NET452)
// .NET Standard <=1.2, .NET Framework <=4.5.2
#else
Console.WriteLine("Run on .NET Standard 1.3+, .NET Framework 4.6+");
#endif
(完)
作者:zyl910 出處:http://www.cnblogs.com/zyl910/ 著作權宣告:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0.轉載請註明出處,本文鏈接:https://www.uj5u.com/net/458327.html
標籤:.NET技术
