• C#利用条件编译判断.NET平台及版本的办法,NET5标准符号清单及使用经验


    作者: 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
    

    (完)

  • 相关阅读:
    SGU 271 Book Pile (双端队列)
    POJ 3110 Jenny's First Exam (贪心)
    HDU 4310 Hero (贪心)
    ZOJ 2132 The Most Frequent Number (贪心)
    POJ 3388 Japanese Puzzle (二分)
    UVaLive 4628 Jack's socks (贪心)
    POJ 2433 Landscaping (贪心)
    CodeForces 946D Timetable (DP)
    Android Studio教程从入门到精通
    Android Tips – 填坑手册
  • 原文地址:https://www.cnblogs.com/zyl910/p/cs_standard_conditional_compilation_symbols.html
Copyright © 2020-2023  润新知