如果目标框架是较新的版本,有一些小的地方可以大大改善我的项目的代码。我希望能够更好地利用C#中的条件编译来根据需要切换它们。
类似的东西:
#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif
这些符号是否免费提供?我是否需要将这些符号作为项目配置的一部分注入?看起来很容易,因为我知道哪个框架是MSBuild的目标。
/p:DefineConstants="NET40"
更新:我的问题是人们如何处理这种情况?你在创造不同的配置吗?你是通过命令行传递常量吗?
答案 0 :(得分:115)
实现此目标的最佳方法之一是在项目中创建不同的构建配置:
<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>
答案 1 :(得分: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>
这取得了TargetFrameworkVersion属性的值,就像“v3.5”一样,取代了“v”和“。”。获得“NET35”(使用新的Property Functions功能)。然后它删除任何现有的“NETxx”值并将其添加到DefinedConstants的末尾。有可能简化这一点,但我没有时间来摆弄。
查看VS中项目属性的“构建”选项卡,您将在条件编译符号部分中看到结果值。在“应用程序”选项卡上更改目标框架版本,然后自动更改符号。然后,您可以按常规方式使用#if NETxx
预处理程序指令。在VS中更改项目似乎没有丢失自定义PropertyGroup。
请注意,这似乎没有给出任何与Client Profile目标选项不同的内容,但这对我来说不是问题。
答案 2 :(得分:14)
我遇到了这些解决方案的问题,可能是因为我的初始常量是由这些属性预先构建的。
<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>
我会发布“高级编译器设置”对话框的屏幕截图(通过单击项目“编译”选项卡上的“高级编译选项...”按钮打开)。但作为一个新用户,我缺乏这样做的代表。如果你能看到屏幕截图,你会看到属性组自动填充的自定义常量然后你会说,“我得告诉我一些。”
编辑:得到令人惊讶的快...谢谢你们!这是截图:
答案 3 :(得分: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>
我认为这种方法非常易读且易懂。
答案 4 :(得分:2)
在.csproj文件中,在现有<DefineConstants>DEBUG;TRACE</DefineConstants>
行之后,添加以下内容:
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace("v",""))' >= '4.0' ">NET_40_OR_GREATER</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace("v",""))' == '4.0' ">NET_40_EXACTLY</DefineConstants>
对Debug和Release构建配置执行此操作。 然后在你的代码中使用:
#if NET_40_OR_GREATER
// can use dynamic, default and named parameters
#endif
答案 5 :(得分:2)
答案 6 :(得分:0)
如果您使用的是.NET Core构建系统,则可以使用其预定义符号(实际上已经与您的示例匹配,并且不需要对find
进行任何更改!):
.csproj
预定义符号列表记录在Developing Libraries with Cross Platform Tools和#if (C# Reference)中:
.NET Framework :
#if NET40 using FooXX = Foo40; #elif NET35 using FooXX = Foo35; #else NET20 using FooXX = Foo20; #endif
,NETFRAMEWORK
,NET20
,NET35
,NET40
,NET45
,NET451
,NET452
,NET46
,NET461
,NET462
,NET47
,NET471
,NET472
.NET标准:
NET48
,NETSTANDARD
,NETSTANDARD1_0
,NETSTANDARD1_1
,NETSTANDARD1_2
,NETSTANDARD1_3
,NETSTANDARD1_4
,NETSTANDARD1_5
,NETSTANDARD1_6
,NETSTANDARD2_0
.NET Core :
NETSTANDARD2_1
,NETCOREAPP
,NETCOREAPP1_0
,NETCOREAPP1_1
,NETCOREAPP2_0
,NETCOREAPP2_1
,NETCOREAPP2_2