使用#if/#endif 块可以在同样源码上生成不同的编译(结果),大多数debug 和release两个版本。但它们决不是我们喜欢用的工具。由于#if/#endif很容易 被滥用,使得编写的代码难于理解且更难于调试。程序语言设计者有责任提供更 好的工具,用于生成在不同运行环境下的机器代码。C#就提供了条件属性 (Conditional attribute)来识别哪些方法可以根据环境设置来判断是否应该被 调用。
(译注:属性在C#里有两个单词,一个是property另一个是 attribute,它们有不是的意思,但译为中文时一般都是译为了属性。property 是指一个对象的性质,也就是Item1里说的属性。而这里的attribute指的是.net 为特殊的类,方法或者property附加的属性。可以在MSDN里查找attribute取得 更多的帮助,总之要注意:attribute与property的意思是完全不一样的。)
这个方法比条件编译#if/#endif更加清晰明白。编译器可以识别 Conditional属性,所以当条件属性被应用时,编译器可以很出色的完成工作。 条件属性是在方法上使用的,所以这就使用你必须把不同条件下使用的代码要写 到不同的方法里去。当你要为不同的条件生成不同的代码时,请使用条件属性而 不是#if/#endif块。
很多编程老手都在他们的项目里用条件编译来检测 先决条件(per-conditions)和后续条件(post-conditions)。
(译注: per-conditions,先决条件,是指必须满足的条件,才能完成某项工作,而 post-conditions,后续条件,是指完成某项工作后一定会达到的条件。例如某 个函数,把某个对象进行转化,它要求该对象不能为空,转化后,该对象一定为 整形,那么:per-conditions就是该对象不能为空,而post-conditions就是该 对象为整形。例子不好,但可以理解这两个概念。)
你可能会写一个私有 方法来检测所有的类及持久对象。这个方法可能会是一个条件编译块,这样可以 使它只在debug时有效。
private void CheckState( )
{
// The Old way:
#if DEBUG
Trace.WriteLine( "Entering CheckState for Person" );
// Grab the name of the calling routine:
string methodName =
new StackTrace( ).GetFrame( 1 ).GetMethod( ).Name;
Debug.Assert( _lastName != null,
methodName,
"Last Name cannot be null" );
Debug.Assert( _lastName.Length > 0,
methodName,
"Last Name cannot be blank" );
Debug.Assert( _firstName != null,
methodName,
"First Name cannot be null" );
Debug.Assert( _firstName.Length > 0,
methodName,
"First Name cannot be blank" );
Trace.WriteLine( "Exiting CheckState for Person" );
#endif
}
使用#if 和#endif编译选项(pragmas),你已经为你的发布版(release)编译出了一个空方 法。这个CheckState()方法会在所有的版本(debug和release)中调用。而在 release中它什么也不做,但它要被调用。因此你还是得为例行公事的调用它而 付出小部份代价。
不管怎样,上面的实践是可以正确工作的,但会导致 一个只会出现在release中的细小BUG。下面的就是一个常见的错误,它会告诉你 用条件编译时会发生什么:
public void Func( )
{
string msg = null;
#if DEBUG
msg = GetDiagnostics( );
#endif
Console.WriteLine( msg );
}