条件编译和框架目标

mck*_*mey 121 c# msbuild .net-4.0 .net-3.5

如果目标框架是较新版本,那么我的项目代码可能会有一些小的地方可以大大改进.我希望能够更好地利用C#中的条件编译来根据需要切换它们.

就像是:

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif
Run Code Online (Sandbox Code Playgroud)

这些符号中的任何一个都是免费的吗?我是否需要将这些符号作为项目配置的一部分注入?看起来很容易,因为我知道哪个框架是MSBuild的目标.

/p:DefineConstants="NET40"
Run Code Online (Sandbox Code Playgroud)

更新:我的问题是人们如何处理这种情况?你在创造不同的配置吗?您是否通过命令行传递常量?

Tod*_*odd 118

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

<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>
Run Code Online (Sandbox Code Playgroud)

在您的一个默认配置中:

<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>
Run Code Online (Sandbox Code Playgroud)

如果没有在其他地方定义,则设置默认值.在上面的例子中,每次构建每个版本时,OutputPath都会为您提供一个单独的程序集.

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

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

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

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

<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />
Run Code Online (Sandbox Code Playgroud)

甚至参考

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

  • 感谢您的回答,但现在VS2010已经包含了一个名为"TargetFrameworkVersion"的新标签,现在对于每个具有条件的属性组,只更改了TargetFrameworkVersion,我们是否还需要所有这些才能使其工作? (7认同)
  • 这篇文章对我有用,但我不擅长MSBuild,需要一段时间才能搞清楚.我做了一个项目,作为一个例子.https://dev6.blob.core.windows.net/blog-images/DualTargetFrameworks.zip (4认同)

小智 43

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

 <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>
Run Code Online (Sandbox Code Playgroud)

这取得了TargetFrameworkVersion属性的值,就像"v3.5"一样,取代了"v"和".".得到"NET35"(使用新的属性函数功能).然后它删除任何现有的"NETxx"值并将其添加到DefinedConstants的末尾.有可能简化这一点,但我没有时间来摆弄.

查看VS中项目属性的"构建"选项卡,您将在条件编译符号部分中看到结果值.在"应用程序"选项卡上更改目标框架版本,然后自动更改符号.然后,您可以#if NETxx按常规方式使用预处理程序指令.在VS中更改项目似乎没有丢失自定义PropertyGroup.

请注意,这似乎没有给出客户端配置文件目标选项的任何不同,但这对我来说不是问题.


Nat*_*ark 15

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

<DefineConstants />
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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


编辑:得到令人惊讶的快...谢谢你们!这是截图:

高级编译器设置


Kev*_*oid 11

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

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif
Run Code Online (Sandbox Code Playgroud)

预定义符号的列表记录在使用跨平台工具#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核心NETCOREAPP、、、、、、、NETCOREAPP1_0NETCOREAPP1_1​​NETCOREAPP2_0NETCOREAPP2_1NETCOREAPP2_2NETCOREAPP3_0