条件编译和框架目标

问题描述 投票:0回答:7

如果目标框架是较新的版本,我的项目代码可能会在一些小地方得到显着改进。我希望能够更好地利用 C# 中的条件编译来根据需要切换这些。

类似:

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif

这些符号中的任何一个都是免费的吗?我需要将这些符号作为项目配置的一部分注入吗?这似乎很容易做到,因为我会知道 MSBuild 的目标框架是哪个。

/p:DefineConstants="NET40"

人们如何处理这种情况?您正在创建不同的配置吗?您是通过命令行传入常量吗?

c# .net-3.5 msbuild .net-4.0
7个回答
121
投票

实现此目的的最佳方法之一是在项目中创建不同的构建配置:

<PropertyGroup Condition="  '$(Framework)' == 'NET20' ">
  <DefineConstants>NET20</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>


<PropertyGroup Condition="  '$(Framework)' == 'NET35' ">
  <DefineConstants>NET35</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>

在您的默认配置之一中:

<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>

如果没有在其他地方定义,它将设置默认值。在上述情况下,每次构建每个版本时,OutputPath 都会为您提供一个单独的程序集。

然后创建一个 AfterBuild 目标来编译不同的版本:

<Target Name="AfterBuild">
  <MSBuild Condition=" '$(Framework)' != 'NET20'"
    Projects="$(MSBuildProjectFile)"
    Properties="Framework=NET20"
    RunEachTargetSeparately="true"  />
</Target>

此示例将在第一次构建后重新编译整个项目,并将 Framework 变量设置为 NET20(编译两者并假设第一个构建是上面的默认 NET35)。每次编译都会正确设置条件定义值。

通过这种方式,如果您不想 #ifdef 文件,您甚至可以排除项目文件中的某些文件:

<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />

甚至参考文献

<Reference Include="Some.Assembly" Condition=" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>

46
投票

到目前为止对我有用的另一种方法是将以下内容添加到项目文件中:

 <PropertyGroup>
    <DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
    <DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
  </PropertyGroup>

这采用 TargetFrameworkVersion 属性的值,类似于“v3.5”,替换“v”和“.”。获取“NET35”(使用新的 Property Functions 功能)。然后,它删除任何现有的“NETxx”值并将其添加到 DefinedConstants 的末尾。也许可以简化这个,但我没有时间去摆弄。

查看 VS 中项目属性的“构建”选项卡,您将在条件编译符号部分看到结果值。在“应用程序”选项卡上更改目标框架版本会自动更改符号。然后,您可以按照通常的方式使用

#if NETxx
预处理器指令。在VS中更改项目似乎并没有丢失自定义的PropertyGroup。

请注意,这似乎不会给您的客户配置文件目标选项带来任何不同,但这对我来说不是问题。


16
投票

我对这些解决方案遇到了问题,可能是因为我的初始常量是由这些属性预先构建的。

<DefineConstants />
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>

Visual Studio 2010 也因分号而抛出错误,声称它们是非法字符。错误消息给了我一个提示,因为我可以看到用逗号分隔的预构建常量,最后是我的“非法”分号。经过一番重新格式化和调整后,我找到了一个适合我的解决方案。

<PropertyGroup>
  <!-- Adding a custom constant will auto-magically append a comma and space to the pre-built constants.    -->
  <!-- Move the comma delimiter to the end of each constant and remove the trailing comma when we're done.  -->
  <DefineConstants Condition=" !$(DefineConstants.Contains(', NET')) ">$(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.Contains(', NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 2.0 ">$(DefineConstants)NET_20_OR_GREATER, </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 3.5 ">$(DefineConstants)NET_35_OR_GREATER</DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.EndsWith(', ')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", "))))</DefineConstants>
</PropertyGroup>

我将发布“高级编译器设置”对话框的屏幕截图(通过单击项目“编译”选项卡上的“高级编译选项...”按钮打开)。但作为一个新用户,我缺乏这样做的代表。如果您可以看到屏幕截图,您会看到属性组自动填充的自定义常量,然后您会说,“我必须得到一些。”


编辑: 获得代表的速度出奇地快..谢谢大家!这是截图:

Advanced Compiler Settings


10
投票

如果您使用 .NET Core 构建系统,则可以使用其预定义符号(实际上已经与您的示例匹配,并且不需要对您的

.csproj
进行任何更改!):

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif

预定义符号列表记录在使用跨平台工具开发库#if(C# 参考)

.NET 框架:

NETFRAMEWORK
,
NET20
,
NET35
,
NET40
,
NET45
,
NET451
,
NET452
,
NET46
,
NET461
,
NET462 
NET47
,
NET471
,
NET472
,
NET48

.NET 标准:

NETSTANDARD
NETSTANDARD1_0
NETSTANDARD1_1
NETSTANDARD1_2
NETSTANDARD1_3
NETSTANDARD1_4
NETSTANDARD1_5
NETSTANDARD1_6
NETSTANDARD2_0
NETSTANDARD2_1

.NET Core:

NETCOREAPP
NETCOREAPP1_0
NETCOREAPP1_1
NETCOREAPP2_0
NETCOREAPP2_1
NETCOREAPP2_2
NETCOREAPP3_0


4
投票

从清除常量开始:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

接下来,构建调试、跟踪和其他常量,例如:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
    <DebugSymbols>true</DebugSymbols>
  <DebugType>full</DebugType>
  <Optimize>false</Optimize>
  <DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
</PropertyGroup>

最后,构建你的框架常量:

<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">
  <DefineConstants>NET10;NET20;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.0' ">
  <DefineConstants>NET10;NET20;NET30;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;NET45;$(DefineConstants)</DefineConstants>
</PropertyGroup>

我认为这种方法非常具有可读性和可理解性。


3
投票

在 .csproj 文件中,在现有

<DefineConstants>DEBUG;TRACE</DefineConstants>
行之后,添加以下内容:

<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '4.0' ">NET_40_EXACTLY</DefineConstants>

对调试和发布构建配置执行此操作。 然后在您的代码中使用:

#if NET_40_OR_GREATER
   // can use dynamic, default and named parameters
#endif

2
投票

@Azarien,您的答案可以与 Jeremy 的答案结合起来,将其保留在一个地方,而不是“调试|发布”等。

对我来说,结合这两种变体效果最好,即使用 #if NETXX 在代码中包含条件,并一次性构建不同的框架版本。

我的 .csproj 文件中有这些:

  <PropertyGroup>
    <DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '3.5' ">
    <DefineConstants>NET35</DefineConstants>
    <OutputPath>bin\$(Configuration)\$(TargetFrameworkVersion)</OutputPath>
  </PropertyGroup>

在目标中:

  <Target Name="AfterBuild">
    <MSBuild Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' "
      Projects="$(MSBuildProjectFile)"
      Properties="TargetFrameworkVersion=v3.5"
      RunEachTargetSeparately="true"  />
  </Target>
© www.soinside.com 2019 - 2024. All rights reserved.