在编译时检测目标框架版本

Mat*_*eld 57 .net c# msbuild extension-methods visual-studio

我有一些使用扩展方法的代码,但是使用VS2008中的编译器在.NET 2.0下编译.为了促进这一点,我不得不声明ExtensionAttribute:

/// <summary>
/// ExtensionAttribute is required to define extension methods under .NET 2.0
/// </summary>
public sealed class ExtensionAttribute : Attribute
{
}
Run Code Online (Sandbox Code Playgroud)

但是,我现在想要包含该类的库也可以在.NET 3.0,3.5和4.0下编译 - 没有'ExtensionAttribute在多个地方定义'警告.

当目标框架版本是.NET 2时,是否有任何编译时指令可用于仅包含ExtensionAttribute?

Jam*_*ing 60

带有'创建N个不同配置'的链接SO问题肯定是一个选项,但是当我需要这个时,我只是添加了条件的DefineConstants元素,所以在我的Debug | x86(例如)之后,现有的DefineConstants for DEBUG; TRACE,我添加了这两个,检查在csproj文件的第一个PropertyGroup中设置的TFV中的值.

<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>
Run Code Online (Sandbox Code Playgroud)

显然,你不需要两者,但它只是提供了eq和ne行为的例子 - #else和#elif也很好:)

class Program
{
    static void Main(string[] args)
    {
#if RUNNING_ON_4
        Console.WriteLine("RUNNING_ON_4 was set");
#endif
#if NOT_RUNNING_ON_4
        Console.WriteLine("NOT_RUNNING_ON_4 was set");
#endif
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我可以在目标3.5和4.0之间切换,它会做正确的事情.

  • 抱歉,我在哪里写这些DefineConstants标签? (14认同)
  • 我知道这已经很老了,但是没有误导?不应该是COMPILING_ON_4吗? (4认同)
  • 打破#if DEBUG预编译指令.马斯洛拥有可行的解决方案.如果你打算破坏它们,请不要提及DEBUG; TRACE常量. (2认同)

And*_*son 34

我有一些建议可以改进到目前为止给出的答案:

  1. 使用Version.CompareTo().测试相等性不适用于以后的框架版本,但尚未命名.例如

    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
    
    Run Code Online (Sandbox Code Playgroud)

    不符合v4.5或v4.5.1,这通常是你想要的.

  2. 使用导入文件,以便只需定义一次这些附加属性.我建议将导入文件保留在源代码管理下,以便更改与项目文件一起传播,而无需额外的努力.

  3. 在项目文件的末尾添加import元素,以使其独立于任何特定于配置的属性组.这还有一个好处,即在项目文件中需要一个额外的行.

这是导入文件(VersionSpecificSymbols.Common.prop)

<!--
******************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) &gt;= 0">$(DefineConstants);NETFX_451</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5'))))   &gt;= 0">$(DefineConstants);NETFX_45</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0'))))   &gt;= 0">$(DefineConstants);NETFX_40</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5'))))   &gt;= 0">$(DefineConstants);NETFX_35</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0'))))   &gt;= 0">$(DefineConstants);NETFX_30</DefineConstants>
    </PropertyGroup>
</Project>
Run Code Online (Sandbox Code Playgroud)

将导入元素添加到项目文件

通过在标记之前的末尾添加来从.csproj文件引用它.

…
    <Import Project="VersionSpecificSymbols.Common.prop" />
</Project>
Run Code Online (Sandbox Code Playgroud)

您需要修复路径以指向放置此文件的公共/共享文件夹.

使用编译时符号

namespace VersionSpecificCodeHowTo
{
    using System;

    internal class Program
    {
        private static void Main(string[] args)
        {
#if NETFX_451
            Console.WriteLine("NET_451 was set");
#endif

#if NETFX_45
            Console.WriteLine("NET_45 was set");
#endif

#if NETFX_40
            Console.WriteLine("NET_40 was set");
#endif

#if NETFX_35
            Console.WriteLine("NETFX_35 was set");
#endif

#if NETFX_30
            Console.WriteLine("NETFX_30 was set");
#endif

#if NETFX_20
             Console.WriteLine("NETFX_20 was set");
#else
           The Version specific symbols were not set correctly!
#endif

#if DEBUG
            Console.WriteLine("DEBUG was set");
#endif

#if MySymbol
            Console.WriteLine("MySymbol was set");
#endif
            Console.ReadKey();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

一个共同的"真实生活"的例子

实现Join(字符串分隔符,IEnumerable字符串)在.NET 4.0之前

// string Join(this IEnumerable<string> strings, string delimiter)
// was not introduced until 4.0. So provide our own.
#if ! NETFX_40 && NETFX_35
public static string Join( string delimiter, IEnumerable<string> strings)
{
    return string.Join(delimiter, strings.ToArray());
}
#endif
Run Code Online (Sandbox Code Playgroud)

参考

属性函数

MSBuild属性评估

我可以根据.NET框架版本制作预处理程序指令吗?

条件编译取决于C#中的框架版本


Mas*_*low 29

属性组只覆盖所以这会为击败你的设置DEBUG,TRACE或任何其他.- 请参阅MSBuild属性评估

此外,如果DefineConstants从命令行设置属性,则在项目文件中对其执行的操作无关紧要,因为该设置将成为全局只读.这意味着您对该值的更改将无提示失败.

维护现有定义常量的示例:

    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">V2</CustomConstants>
    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">V4</CustomConstants>
    <DefineConstants Condition=" '$(DefineConstants)' != '' And '$(CustomConstants)' != '' ">$(DefineConstants);</DefineConstants>
    <DefineConstants>$(DefineConstants)$(CustomConstants)</DefineConstants>
Run Code Online (Sandbox Code Playgroud)

本节必须遵循任何其他定义的常量,因为这些常量不太可能以相加的方式设置

我只定义了那些2,因为这主要是我对我的项目感兴趣的,ymmv.

另请参见:常见的MsBuild项目属性

  • 如果在最后一行放置分号,则可以省略第三行:`$(DefineConstants); $(CustomConstants)`.在空常量变量的情况下,额外的分号似乎不会引起任何问题. (3认同)

Arn*_*ion 6

目标框架的预定义符号现已内置到该dotnet工具以及VS 2017及更高版本所使用的MSBuild版本中。有关完整列表,请参见https://docs.microsoft.com/zh-cn/dotnet/standard/frameworks#how-to-specify-target-frameworks

#if NET47
Console.WriteLine("Running on .Net 4.7");
#elif NETCOREAPP2_0
Console.WriteLine("Running on .Net Core 2.0");
#endif
Run Code Online (Sandbox Code Playgroud)


小智 5

我想提供更新的答案,以解决一些问题.

如果您设置DefineConstants而不是CustomConstants,您将最终在条件编译符号调试命令行中,在一些框架版本切换之后,具有重复的条件常量(即:NETFX_451; NETFX_45; NETFX_40; NETFX_35; NETFX_30; NETFX_20; NETFX_35; NETFX_30 ; NETFX_20).这是VersionSpecificSymbols.Common.prop,它可以解决任何问题.

<!--
*********************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
Author: Lorenzo Ruggeri (lrnz.ruggeri@gmail.com)
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Choose>
    <When Condition=" $(TargetFrameworkVersion) == 'v2.0' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <When Condition=" $(TargetFrameworkVersion) == 'v3.0' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <When Condition=" $(TargetFrameworkVersion) == 'v3.5' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_35</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) &gt;= 0">$(CustomConstants);NETFX_451</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) &gt;= 0">$(CustomConstants);NETFX_45</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) &gt;= 0">$(CustomConstants);NETFX_40</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) &gt;= 0">$(CustomConstants);NETFX_35</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) &gt;= 0">$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('2.0')))) &gt;= 0">$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  <PropertyGroup>
    <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
  </PropertyGroup>
</Project>
Run Code Online (Sandbox Code Playgroud)