何时在 .NET 中使用预处理器指令?

27

我认为这是一个简单的问题,因此我想我可能错过了一些显而易见的东西。我实际上从不使用预处理器指令,但我正在查看某人的代码,并认为这是我应该熟悉的内容。

因此,我查看了msdn示例这里的代码:

#define DEBUG
// ...
#if DEBUG
    Console.WriteLine("Debug version");
#endif

我的两个问题是:

  • 在上面的示例中,为什么要定义DEBUG?我认为它是根据编译为调试版本还是发布版本来设置的?
  • 看另一个例子,它有#define MYTEST,然后根据是否“定义”将内容写入控制台,但这与使用变量有何不同?我错过了什么?
7个回答

20

我实际上建议使用条件属性而不是内联的# if语句。

[Conditional("DEBUG")]
private void DeleteTempProcessFiles()
{
}

这种方式更加简洁易读,因为你不需要在代码中使用#if和#else。这种样式不仅在正常的代码编辑中更少出错,而且逻辑流程错误也更少。


1
我不知道这种风格。谢谢你的发布。 - Shekhar
1
需要注意的是,无论条件是否满足,该方法始终会被发出。该属性仅影响是否会发出对此方法的调用;这使得可以在库中定义一个方法并给它[Conditional("DEBUG")],从而影响针对该库的编译。 - cdhowie
很好,这里还有更多关于此的信息:https://dev59.com/fG865IYBdhLWcg3wfeqU - mstaffeld
该属性存在许多限制,这很烦人(没有构造函数(公平),但方法必须是void)。 - Frank V

9
通常,可选/条件编译符号将由构建脚本提供。除非是非常调试的代码(如果你知道我的意思),否则很少看到 #define。
关于使用变量;我经常使用这样的条件来处理必须在不同运行时(mono、cf、silverlight等)上运行的代码。一个变量是不够的,因为代码不能针对错误的平台进行编译(缺少类型/方法等)。
在所示例子中,我可能只会使用 Debug.WriteLine;因为它带有 [Conditional("DEBUG")] 修饰,如果在构建时未定义 DEBUG,则所有对它的调用都会自动删除。

5
在上面的例子中,为什么要定义DEBUG?我印象中是在编译调试模式下设置的,对吗?
可能是因为这是示例代码。 它旨在演示#define和相关函数的工作原理。 我不希望您在源文件中定义这样的符号,除非它是用于快速测试。
看看另一个例子,它有“#define MYTEST”,然后根据它是否被‘定义’写入控制台,但这与使用变量有什么区别? 我错过了什么吗?
如果在编译时未定义MYTEST,则编译器实际上不会发出#if#endif块之间的代码。 因此,结果IL将更小。
此外,请注意,这些不是C#中的预处理指令。

1
MSDN 将它们称为 "预处理器指令"。 - Ben Voigt
但它们并不是。来自同一篇文章:“编译器没有单独的预处理器”。 - cdhowie
4
它们可能更准确地被称为“条件编译符号”。它们类似于在C++中发现的那些,但是C#编译器没有单独的预处理器。(http://blogs.msdn.com/b/csharpfaq/archive/2004/03/09/86979.aspx) - Cody Gray

4

如果您使用变量,那么您的所有代码都会被编译,当您使用预处理指令时,只有部分代码包含在可执行文件/dll中。


4

我想举个例子,说明在我的项目中我使用了预处理指令。

我的程序会在磁盘上创建许多中间文件。我使用了#DEBUG指令,只有在我的项目处于发布模式时才删除这些文件,否则我保留这些文件,以便查看这些中间文件并确定内部发生了什么。

当我的应用程序在生产服务器上工作时,我会在发布模式下构建项目,以便在处理完成后删除这些文件。

#if (DEBUG==false)
    deleteTempFiles()
#endif

3
通常我会写成i#f !DEBUG,意思和之前一样,只是稍微简洁了一点。 - Neil N
@Neil,你的方法也是正确的,但我更喜欢DEBUG=false,因为它更清晰易懂。 - Shekhar
我强烈建议不要在调试配置中禁用任何生产代码,因为它可能会产生你在调试时永远无法获得的副作用(是的,我知道它不应该这样,但是我们都知道生活,不是吗?)。对于你的例子,我会采取另一种方式:如果设置了DEBUG,则在覆盖之前将临时文件复制到其他位置进行检查。尽管我承认有些情况下你必须模拟一些东西(事务服务等)。 - ofi

2

我有一些代码,需要在使用 Mono 环境而不是 CLR 环境时进行不同的处理 - 因此,在我的一些模块中有 Mono 指令。我认为这比调试更好地解释了这个问题。


如果代码需要不同的处理方式,您应该在运行时检查是否在Mono下运行,并相应地更改行为;这样,您仍然可以将一个二进制文件发布到所有平台。(if (Type.GetType("System.MonoType") != null) { ... }是测试是否在Mono运行时的推荐方法。请注意,运行在Mono上并不意味着运行在Linux上;Mono也可用于Windows以及其他平台,如OS X。) 然而,通常测试您所在的运行时是一个坏主意。如果您正在解决Mono的错误,请测试错误本身是否存在。 - cdhowie

1

我已经在很多事情上使用它了。仅在调试版本中需要的调试消息;清除临时文件;包括诊断函数或操作。


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