在编译时检测目标框架版本
我有一些使用扩展方法的代码,但使用VS2008中的编译器在.NET 2.0下编译。 为了方便这个,我不得不声明ExtensionAttribute:
/// <summary> /// ExtensionAttribute is required to define extension methods under .NET 2.0 /// </summary> public sealed class ExtensionAttribute : Attribute { }
但是,现在我想要包含该类的库也可以在.NET 3.0,3.5和4.0下编译 – 没有“ExtensionAttribute在多个地方定义”的警告。
有什么编译时间指令我可以用来只包括ExtensionAttribute当框架版本的目标是.NET 2?
“创buildN个不同的configuration”链接的SO问题当然是一种select,但是当我需要这个时,我只是添加了条件的DefineConstants元素,所以在我的Debug | x86(例如)现有的DEBUG; TRACE的DefineConstants之后,我添加了这两个,检查在csproj文件的第一个PropertyGroup中设置的TFV中的值。
<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants> <DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>
显然,你不需要这两个,但它只是在那里给出了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 } }
然后我可以在3.5和4.0之间切换,它会做正确的事情。
属性组只能被覆盖,所以这会将您的设置删除, 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>
这部分必须在任何其他定义的常量之后,因为这些常量不太可能以叠加方式设置
我只定义了这两个,因为这大部分是我对我的项目ymmv感兴趣的。
另请参见: 常见的MsBuild项目属性
对于迄今为止给出的答案,我有几点build议:
-
使用Version.CompareTo()。 对于以后的框架版本,testing是否相等将不起作用,但尚未命名。 例如
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
将不匹配v4.5或v4.5.1,这通常是你想要的。
-
使用一个导入文件,以便这些额外的属性只需要定义一次。 我build议将导入文件保存在源代码控制之下,以便将更改与项目文件一起传播,无需额外的工作。
-
在项目文件的末尾添加导入元素,以使其独立于任何特定于configuration的属性组。 这也有利于您的项目文件中需要一个额外的行。
这里是导入文件(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')))) >= 0">$(DefineConstants);NETFX_451</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(DefineConstants);NETFX_45</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(DefineConstants);NETFX_40</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(DefineConstants);NETFX_35</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(DefineConstants);NETFX_30</DefineConstants> </PropertyGroup> </Project>
将导入元素添加到项目文件
通过在标签之前的末尾添加,从.csproj文件引用它。
… <Import Project="VersionSpecificSymbols.Common.prop" /> </Project>
您需要修复path以指向放置此文件的公共/共享文件夹。
使用编译时间符号
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(); } } }
一个共同的“现实生活”的例子
实现联接(string分隔符,IEnumerablestring)在.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
参考
属性函数
MSBuild物业评估
我可以做一个预处理器指令依赖于.NET框架版本吗?
条件编译取决于C#中的框架版本
我想贡献一个更新的答案,解决了一些问题。
如果设置DefineConstants而不是CustomConstants,则最终将在条件编译符号debugging命令行中,在一些框架版本切换之后,重复条件常量(即: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')))) >= 0">$(CustomConstants);NETFX_451</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(CustomConstants);NETFX_45</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(CustomConstants);NETFX_40</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(CustomConstants);NETFX_35</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(CustomConstants);NETFX_30</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('2.0')))) >= 0">$(CustomConstants);NETFX_20</CustomConstants> </PropertyGroup> </Otherwise> </Choose> <PropertyGroup> <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants> </PropertyGroup> </Project>
使用reflection来确定类是否存在。 如果是,则dynamic创build并使用它,否则使用可定义的.Net2解决scheme类,但不能用于所有其他.net版本。
这里是我用于一个AggregateException
代码,它只是.Net 4和更大的代码:
var aggregatException = Type.GetType("System.AggregateException"); if (aggregatException != null) // .Net 4 or greater { throw ((Exception)Activator.CreateInstance(aggregatException, ps.Streams.Error.Select(err => err.Exception))); } // Else all other non .Net 4 or less versions throw ps.Streams.Error.FirstOrDefault()?.Exception ?? new Exception("Powershell Exception Encountered."); // Sanity check operation, should not hit.