是否可以有条件地编译为 .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 跟踪其进度。

【讨论】:

你甚至不需要有条件地这样做,你可以定义:&lt;DefineConstants&gt;NETFX$(TargetFrameworkVersion.Replace("v", "").Replace(".", "_"));$(DefineConstants)&lt;/DefineConstants&gt;【参考方案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&lt;T1, T2&gt;,所以我只写您自己的简单的,以便其他开发人员可以轻松呼吸:)

【讨论】:

KeyValuePair 不是结构吗?【参考方案4】:

如果您使用的是 .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, NET451NET45NET40NET35NET20

.NET 标准: NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0

.NET 5+(和 .NET Core): NETNET6_0NET6_0_androidNET6_0_iosNET6_0_MACOSNET6_0_MACCATALYSTNET6_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 版本?的主要内容,如果未能解决你的问题,请参考以下文章

用于 C++ 的 C# 包装器,但仅编译为静态库

Angular 6打字稿与KotlinJs集成

学c++看啥书

Linux内核编译错误

将现有 C# 代码编译为 WebAssembly

Unity高级游戏地编案例