是否可以有条件地编译为 .NET Framework 版本?
Posted
技术标签:
【中文标题】是否可以有条件地编译为 .NET Framework 版本?【英文标题】:Is it possible to conditionally compile to .NET Framework version? 【发布时间】:2010-11-29 19:30:25 【问题描述】:我记得在使用 MFC 时,您可以通过检查 _MFC_VER
宏来支持 MFC 框架的多个版本。
我现在正在使用 .NET 4 做一些事情,并希望在几个地方使用 Tuple,但仍保持其他所有内容与 3.5 兼容。
我想做类似的事情:
#if DOTNET4
public Tuple<TSource, TResult> SomeMethod<TSource, TResult>()...
#else
public KeyValuePair<TSource, TResult> SomeMethod<TSource, TResult>()...
#endif
【问题讨论】:
也许投赞成票:connect.microsoft.com/VisualStudio/feedback/details/679124/… Conditional Compilation and Framework Targets的可能重复 这个用户声音不太一样,反正是关闭的。我找不到任何其他与框架版本相关的内容,所以我创建了一个,visualstudio.uservoice.com/forums/121579-visual-studio/…。请点赞。 【参考方案1】:在您的 .csproj(或 .vbproj,理论上)中定义自定义编译符号时需要注意一个重要警告:它们会覆盖所有先前定义的编译符号。例如,考虑 MSBuild sn-p:
<PropertyGroup Condition="'$(TargetFrameworkVersion)' == 'v4.0'">
<DefineConstants>$(DefineConstants);DOTNET_40</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>ITS_CLOBBERING_TIME</DefineConstants>
</PropertyGroup>
第二个DefineConstants 元素,正如它的值所暗示的那样,会破坏DefineConstants 的第一个值。为避免这种情况,您需要将第二个 DefineConstants 元素重写为如下所示:
<DefineConstants>$(DefineConstants);ITS_CLOBBERING_TIME</DefineConstants>
此外,您还需要将其放置在定义的 PropertyGroup 中在所有其他 PropertyGroups 中,因为 Visual Studio 2010 当前添加了自定义编译符号,它会破坏任何其他自定义您定义的编译符号是否放置在 Visual Studio 放下其定义之前。我已经向微软提交了这个问题。您可以在Microsoft Connect 跟踪其进度。
【讨论】:
你甚至不需要有条件地这样做,你可以定义:<DefineConstants>NETFX$(TargetFrameworkVersion.Replace("v", "").Replace(".", "_"));$(DefineConstants)</DefineConstants>
【参考方案2】:
没有可以使用的内置预编译器常量。但是,在 VS 中创建自己的构建配置很容易,每个配置都有自己的一组定义的常量,当然还有一个目标框架版本。很多人这样做是为了有条件地编译基于 32 位或 64 位的差异。
【讨论】:
我想知道为什么他们不像 DEBUG 和 CODE_ANALYSIS 那样构建这些。 dkackman:Eric Lippert 总是说这些功能不是自己构建的;该功能不会仅仅因为没有人设计、指定、实施、测试、记录和发布该功能而存在。见blogs.msdn.com/ericlippert/archive/2009/06/22/… 构建配置不包括平台和引用程序集。您必须为每个平台配置不同的项目。 @configurator:公平点。对于最常见的非构建配置类型的事情(即必须在项目级别处理的事情并且是已经是 vs.net 一部分的模式)来说,这似乎是一件合乎逻辑的事情。一个紧凑的框架项目定义例如,PocketPC 会自动进行。 @dkackman:我不认为 DEBUG 和 CODE_ANALYSIS 是内置的,至少如果您使用调试左右编译,则不会自动定义。例如:当您运行“调试”配置时,在 csproj(即 msbuild 文件)中显式设置了 DEBUG。【参考方案3】:顺便说一句,你的条件编译代码会让遇到它的程序员感到沮丧。
已编辑,基于 cmets
编写自己的类可能会更好,这样你就可以保证它会做什么,并且你没有任何奇怪的签名或继承问题:
public class Pair<TSource, TResult>
public TSource Source get; set;
public TResult Result get; set;
public Pair()
public Pair(TSource source, TResult result)
Source = source;
Result = result;
// Perhaps override Equals() and GetHashCode() as well
与往常一样,权衡使用内置内容与推出您自己的代码是一件好事。一般来说,这意味着问自己,“我可以维护和支持这段代码吗?”与“开箱即用的代码是否满足我的需要?”
在这种情况下,由于不能保证您拥有Tuple<T1, T2>
,所以我只写您自己的简单的,以便其他开发人员可以轻松呼吸:)
【讨论】:
KeyValuePair如果您使用的是 .NET Core 构建系统,则可以使用其预定义的符号:
#if NET40
public Tuple<TSource, TResult> SomeMethod<TSource, TResult>()...
#else
public KeyValuePair<TSource, TResult> SomeMethod<TSource, TResult>()...
#endif
预定义符号列表记录在Developing Libraries with Cross Platform Tools 和#if (C# Reference):
.NET Framework:
NETFRAMEWORK
,NET48
,NET472
,NET471
,NET47
,NET462
,NET461
,NET46
,NET452
,NET451
、NET45
、NET40
、NET35
、NET20
.NET 标准:
NETSTANDARD
,NETSTANDARD2_1
,NETSTANDARD2_0
,NETSTANDARD1_6
,NETSTANDARD1_5
,NETSTANDARD1_4
,NETSTANDARD1_3
,NETSTANDARD1_2
,NETSTANDARD1_1
,NETSTANDARD1_0
.NET 5+(和 .NET Core):
NET
、NET6_0
、NET6_0_android
、NET6_0_ios
、NET6_0_MACOS
、NET6_0_MACCATALYST
、NET6_0_TVOS
、@ 987654355@,NET5_0
,NETCOREAPP
,NETCOREAPP3_1
,NETCOREAPP3_0
,NETCOREAPP2_2
,NETCOREAPP2_1
,NETCOREAPP2_0
,NETCOREAPP1_1
,NETCOREAPP1_0
【讨论】:
使用.NET 5.0(6.0)是一些额外的预定义符号:net,net6_0,net6_0_android,net6_0_ios,net6_0_macos,net6_0_mactalyst,net6_0_0_0_0_0_0,net6_0_windows,net5_0,netcoreApp,netcoreapp3_1,netcoreapp3_0,netcoreapp2_2,netcoreapp2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0 --- 见上面的链接(#if (C# Reference))由 Kevinoid 提供 感谢@Valvestino!答案已更新。【参考方案5】:由于您应该有不同的项目,因此您可以有部分类,并且只引用每个项目所需的具有特定逻辑的类:
类名.cs 公共部分类名 ...
类名.40.cs 公共部分类名 公共元组 SomeMethod()...
类名.35.cs 公共部分类名 公共 KeyValuePair SomeMethod()...
【讨论】:
以上是关于是否可以有条件地编译为 .NET Framework 版本?的主要内容,如果未能解决你的问题,请参考以下文章