Effective C#原则4:用条件属性而不是#if预编译块

使用#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 );
}

时间: 2025-01-24 12:09:45

Effective C#原则4:用条件属性而不是#if预编译块的相关文章

Effective C#原则41:选择DataSet而不是自定义的数据结构

因为两个原则,把DataSet的名声搞的不好.首先就是使用XML序列化的 DataSet与其它的非.Net代码进行交互时不方便.如果在Web服务的API中使用 DataSet时,在与其它没有使用.Net框架的系统进行交互时会相当困难.其次, 它是一个很一般的容器.你可以通过欺骗.Net框架里的一些安全类型来错误 DataSet.但在现代软件系统中,DataSet还可以解决很多常规的问题.如果你明 白它的优势,避免它的缺点,你就可以扩展这个类型了. DataSet类设计 出来是为了离线使用一些存储

Effective C#原则1:尽可能的使用属性(property),而不是数据成员(field)

我们的目标:尽可能编写出运行效率更高,更健壮,更容易维护的C#代码. 原则一:尽可能的使用属性(property),而不是数据成员(field). Always use properties instead of accessible data members. 出于以下几点原因,请在设计类时,尽可能的使用属性,而不 是成员. 1..Net对属性的支持远远大于对成员的支持,你可以对属性进 行数据绑定,设计时说明等很多数据成员不被支持的内容.看看.net里的属性面 板,你会明白的. 2.数据安全性

Effective C#原则24:选择申明式编程而不是命令式编程

与命令式编程相比,申明式编程可以用更简单,更清楚的方法来描述软件的 行为.申明式编程就是说用申明来定义程序的行为,而不是写一些指令.在C#里 ,也和其它大多数语言一样,你的大多数程序都是命令式的:在程序中写一个方 法来定义行为.在C#中,你在编程时使用特性就是申明式编程.你添加一个特性 到类,属性,数据成员,或者是方法上,然后.Net运行时就会为你添加一些行为 .这样申明的目的就是简单易用,而且易于阅读和维护. 让我们以一个 你已经使用过的例子开始.当你写你的第一个ASP.Net Web服务时,

Effective C#原则35:选择重写函数而不是使用事件句柄

很多.Net类提供了两种不同的方法来控制一些系统的事件.那就是,要么添 加一个事件句柄:要么重写基类的虚函数.为什么要提供两个方法来完成同样的 事情呢?其实很简单,那就是因为不同的情况下要调用为的方法.在派生类的内 部,你应该总是重写虚函数.而对于你的用户,则应该限制他们只使用句柄来响 应一些不相关的对象上的事件. 例如你很了一个很不错的Windows应用程 序,它要响应鼠标点下的事件.在你的窗体类中,你可以选择重写OnMouseDown ()方法: public class MyForm :

Effective C#原则12:选择变量初始化而不是赋值语句

(译注:根据我个人对文章的理解,我把initializer译为:初始化器,它是 指初始化语法,也就是在一个类里声明变量的同时,直接创建实例值的方法. 例:object m_o = new object();如果这段代码不在任何函数内,但在 一个类里,它就是一个初始化器,而不管你是把它放在类的开始还以结尾.) 一些类经常不只一个构造函数.时间一长,就难得让它的成员变量以及 构造函数进行同步了.最好的确保这样的事不会发生的方法就是:在声明就是的 时间就直接初始化,而不是在每个构造函数内进行赋值.而且

Effective C#原则19:选择定义和实现接口而不是继承

抽象类在类的继承中提供了一个常规的"祖先".一个接口描述 了一个可以被其它类型实现的原子级泛型功能.各有千秋,却也不尽相同.接口 是一种合约式设计:一个类型实现了某个接口的类型,就必须实现某些期望的方 法.抽象类则是为一个相关类的集合提供常规的抽象方法.这些都是老套的东西 了:它是这样的,继承就是说它是某物(is a,),而接口就是说它有某个功能 (behaves like.)! 这些陈词滥调已经说了好久了,因为它们提供了说明,同时 在两个结构上描述它们的不同:基类是描述对象是什么,接

Effective C#原则2:为你的常量选择readonly而不是const

对于常量,C#里有两个不同的版本:运行时常量和编译时常量. 因为 他们有不同的表现行为,所以当你使用不当时,将会损伤程序性能或者出现错误 . 两害相权取其轻,当我们不得不选择一个的时候,我们宁可选择一个 运行慢一点但正确的那一个,而不是运行快一点但有错误的那个.基于这个理由 ,你应该选择运行时常量而不是编译时常量(译注:这里隐藏的说明了编译时常 量效率更高,但可能会有错误). 编译时常量更快更直接,但在可维护性 上远不及运行时常量.保留编译时常量是为了满足那些对性能要求克刻,且随着 程序运行时间

Effective C#原则3:选择is或者as操作符而不是做强制类型转换

C#是一个强数据类型语言.好的编程实践意味着当可以避免从一种数据类型 强制转化为另种数据类型时,我们应该尽我们的所能来避免它.但在某些时候, 运行时类型检测是不可避免的.在C#里,大多数时候你要为调用函数的参数使用 System.Object类型,因为Framwork已经为我们定义了函数的原型.你很可能要 试图把那些类型进行向下转化为其它类型的接口或者类.你有两个选择:用as运 算符,或者,采用旧式的C风格,强制转换.(不管是哪一种,)你还必须对变量 进行保护:你可以试着用is进行转换,然而再用

Effective C#原则45:选择强异常来保护程序

当你抛出异常时,你就在应用程序中引入了一个中断事件.而且危机到程序 的控制流程.使得期望的行为不能发生.更糟糕的是,你还要把清理工作留给最 终写代码捕获了异常的程序员.而当一个异常发生时,如果你可以从你所管理的 程序状态中直接捕获,那么你还可以采取一些有效的方法.谢天谢地,C#社区不 须要创建自己的异常安全策略,C++社区里的人已经为我们完成了所有的艰巨的 工作.以Tom Cargill的文章开头:"异常处理:一种错误的安全感觉, " 而且Herb Sutter,Scott Meyer