【问题标题】:Is it possible to conditionally compile to .NET Framework version?是否可以有条件地编译为 .NET Framework 版本?
【发布时间】: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

【问题讨论】:

标签: c# .net .net-4.0 conditional-compilation


【解决方案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(){...} }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    相关资源
    最近更新 更多