C# 编译器csc.exe 和C# 语言本身不公开任何conditional compilation 的预定义常量。 Visual Studio 只添加了DEBUG 和TRACE 值,可以通过IDE 进行配置。 IDE 还允许您添加自己的任意符号,但由于这些符号本质上是固定(不变)值,因此该功能的用途有限。
可以通过手动编辑您的.csproj 项目文件来设置更强大的自定义选项。您可以根据MSBuild中提供的大量环境和配置信息将conditions这里设置为selectively propagate条件编译符号到C#中(参见here和here,但原则上可以没有完整列表,因为不同的组件随意贡献元数据ad-hoc)。
让我们考虑一个工作示例。有条件编译有用的一种情况是,如果您想编写适应构建期间发现的任何工具的代码。通过这种方式,您可以利用最新的语言功能,同时仍然保留在使用旧工具的机器上编译的能力,正如预期的那样,这些工具会拒绝外来语法和/或关键字。对于 Visual Studio 2017 中 C# 7.0 的特殊情况,我们可以修改 .csproj 如下:
.csproj 文件(摘录):
您还可以识别每个较旧的 C# 编译器,并在此过程中优雅地降级。检测 .NET Framework 版本也是如此(在 StackOverflow 上经常请求 [1]
[2]
[3]
[4]) 和任何其他环境构建条件。这些留给读者练习,但如果您想从上面复制/粘贴突出显示的行,这里是文本版本。作为对屏幕截图的更新,我在此处为条件表达式添加了单引号(即使没有它们似乎一切正常)
<DefineConstants Condition="'$(VisualStudioVersion)'=='15'">CSHARP7</DefineConstants>
<!-- ... -->
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
<!-- ... -->
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
无论如何,通过这种方式,您现在可以使用#if… #elif… #else… #endif 编写条件 C# 代码。继续示例案例,下面的代码使用新的元组语法(仅在 C# 7 中可用)来交换数组元素。顺便说一句,元组版本不仅更简洁和/或优雅;它还可以生成出色的 CIL 代码:
#if CSHARP7
(rg[i], rg[j]) = (rg[j], rg[i]); // Swap elements: tuple syntax
#else
var t = rg[i]; // Swap elements: clunky
rg[i] = rg[j];
rg[j] = t;
#endif
请注意,Visual Studio IDE确实在各个方面都能正确处理您的手动 .csproj 自定义设置。鉴于我之前展示的.csproj,IDE 代码编辑器可以正确识别和评估条件编译,以用于IntelliSense、refactoring、“调光”非活动代码块等。
我还提到MSBuild 有一个可用的信息宝库,其中$(VisualStudioVersion) 只是一个例子。不幸的是,要找出哪些值可用以及它们在构建时可能具有哪些值并不容易。一个技巧是将 C++ 项目与 C# 项目一起临时放入您的 Visual Studio 解决方案(如果您还没有)。如果您右键单击此.vcxproj 的项目属性,然后在C/C++ 页面上查看(例如)“其他包含目录”,当您单击时,最右侧会出现一个下拉菜单编辑:
您将看到一个带有“宏”按钮的对话框,您可以单击该按钮来获取所有可用 MSBuild 变量的列表以及根据当前在 IDE 中选择的平台和配置的预期值。不要忽略列表底部的well-known item metadata 字段(以% 为前缀)。
您可以通过此屏幕截图中滚动条拇指的大小来了解这里有多少内容。 (它们按字母顺序列出;我只是滚动到“P”部分的这一部分,因为它包含的个人信息很少。)然而,重要的是要注意,(可用的)变量及其值在构建过程,因此您可能会在此列表中找到您的.csproj 不可用的项目在处理时。
在构建过程中和整个构建过程中找出哪些属性值可用的另一种方法是将 MSBuild“输出详细程度”设置为“详细”,然后重新构建。
构建完成后,在 Visual Studio 输出窗口中检查构建日志的顶部,您将看到可用属性名称的列表以及它们的初始值。