C# 预处理器区分操作系统

40

C#中,是否可以使用预处理器区分操作系统?例如:

#if OS_WINDOWS
//windows methods
#elif OS_MAC
//mac  methods
#elif OS_LINUX
//linux methods
#endif

2
C# 的整个意义在于,您不需要为不同的平台重新编译。 - SLaks
1
但是你可能需要以不同的方式配置事物。如果两个配置值都在设置文件中,你需要知道使用哪一个。 - Jesse Chisholm
5个回答

57

你所要求的是可行的,但需要一些工作。

  1. 在你的 csproj 中定义一个预处理器变量。

    <PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
      <DefineConstants>_WINDOWS</DefineConstants>
    </PropertyGroup>
    
  2. 在您的代码中使用它

  3. #if _WINDOWS
      // your windows stuff
    #else
      // your *nix stuff
    #endif
    

    当您有依赖于操作系统的常量时(例如本地库名称),我发现这种技术很有用。


17
很遗憾,你不能这样做。这是有道理的:如果你编译为AnyCPU,那么你的程序可以在任何平台上运行。
你可以创建多个项目配置,在其中设置你想要的#define(在项目的属性生成条件编译符号中)。
但也许这是一个XY问题……通常情况下,你不需要这样做,而且你可以接受一个

if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{

}
else if (Environment.OSVersion.Platform == PlatformID.MacOSX)
{

}
else if (Environment.OSVersion.Platform == PlatformID.Unix)
{

}

1
这将留下 PlatformID.Win32NT。 - stijn

14
自从MSBuild 15版本以后,就有了一个名为“IsOSPlatform()”的属性函数。它可以用来在项目文件(*.csproj)中定义特定于操作系统的常量。
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
  <DefineConstants>OS_WINDOWS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
  <DefineConstants>OS_LINUX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('FreeBSD'))">
  <DefineConstants>OS_FREEBSD</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('OSX'))">
  <DefineConstants>OS_MAC</DefineConstants>
</PropertyGroup>

这些常量可以在预处理指令中进行检查,例如:

#if OS_WINDOWS
  // Windows-specific code
#elif OS_LINUX
  // Linux-specific code
#elif OS_FREEBSD
  // FreeBSD-specific code
#elif OS_MAC
  // Mac-specific code
#endif

我尝试了这个,特别是我需要 OS_WINDOWS 来获取我只想在 Windows 上使用的某些功能。当我构建 Windows 版本时它完美地工作,但是将其发布到其他平台,如 Linux,则会出现以下错误输出:“我们无法确定错误的原因”,你有什么想法或如何解决? - David Shnayder
@DavidShnayder 我在 Windows 10 和 Debian 11.4 上使用 .NET 6,它可以正常工作。 - kol
@DavidShnayder 首先,我会删除有问题的预处理指令并手动应用它们的效果(添加/删除代码的受影响部分)。如果您无法通过此方法找到问题,则可以尝试使用'$(OS)' == 'Windows_NT'条件而不是基于IsOSPlatform的方法。 - kol
根据文档显示,Mac的平台字符串应为MacOS,而不是OSX - undefined

2

不要误解,编译器只运行一次,但是相同的二进制输出可以用于多台机器。

现在您可以在编译时指定任何符号-因此,您可以轻松地编译三次并每次传递不同的预处理器符号。

如果您不需要任何编译时更改,则可以使用Environment.OSVersion来检测您正在运行的操作系统。


如果我需要在编译时进行更改怎么办?比如说,我不想让我的Windows设置包含与Linux相关的二进制文件,反之亦然。同时,我也不想使用反射。理想情况下,我想在csproj文件中使用类似多目标和条件引用的东西(基于RID)。 - undefined
@isxaker: 我不知道你是否可以为不同的运行时指定不同的预处理器符号(而不是不同的目标框架)。我猜你可以有(比如说)net6-windows TFM 和单独的 net6-linux... - undefined
这就是关键 - 与net6-windows不同,没有net6-linux。 - undefined
1
@isxaker:我猜你可以有条件地说“net6.0但不是net6.0-windows”?感觉值得提出一个新问题,详细说明你想要实现的目标。 - undefined

1
截至2023年,保护操作系统特定代码在运行时的推荐方式是使用OperatingSystem类。
if (OperatingSystem.IsLinux()) // standard guard examples
{
    LinuxOnlyApi();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接