Item12: Prefer Variable Initializers to Assignment Statements
一般来说一个类会有不止一个构造函数,有的时候会因此而造成成员变量和构造函数的脱节。解决这种问题的最好方法就是当变量声明时就对其进行初始化,而不是初始化工作放到构造函数中。
我们应当在声明成员变量的时候就对其进行初始化。
{
private ArrayList _coll = new ArrayList();
}
不论在MyClass类型中的有多少个构造函数,_coll都会被初始化。编译器在生成类的构造函数之前就会先执行我们为这些成员变量定义的初始化工作。当我们添加一个新的构造函数时,即便什么也不做,_coll还是得到了适当的初始化。这样做的另一个好处就在于当我们需要添加一个新的成员变量时,我们不必在每一个构造函数中添加其初始化代码。非常重要的是,我们这些初始化工作也会被编译器添加到默认的构造函数中去。即便我们没有为我们的类型定义任何构造函数,编译器也会为我们生成默认的构造函数。
使用初始化声明是很简洁的。这些变量初始化的声明会放到我们的类型的构造函数之前。它们会在基类构造函数之前被运行。而它们初始化的顺序则是根据我们的声明顺序而来。
在定义时就使用初始化声明成员变量是避免出现未初始化成员的好方法,但这不是完美的。有三种情况下我们不应当在定义变量的时候就使用初始化声明。
第一种情况是在我们要声明成员对象为“0”或null时。在我们的构造函数运行之前,系统默认的初始化会为我们所有的成员变量设为“0”值。这种设“0”操作是通过直接对内存操作而得到的。在这之后,任何额外的设“0”操作都是多余的,然而编译器并不知情,这种额外的操作还会进行一次。这样做是没有意义的,只会降低程序的效率,特别是对于值类型来说。
MyValType _myVal2 = new MyValType();
上例中两个声明的变量都是“0”。第一个是通过对内存进行设置而得到“0”值,第二个是通过IL设置initobj来得到的。第二种方法会涉及boxing和unboxing操作,这会浪费额外的时间。
第二种情况是当我们为同一个成员变量创建了多个不同的初始化时。只有在所有构造函数中声明的变量相同时,我们才能使用初始化来声明对象。下面的MyClass类中的两种构造函数声明了两种不同的ArrayList:
{
private ArrayList _coll = new ArrayList();
MyClass()
{
}
MyClass(int size)
{
_coll = new ArrayList(size);
}
}
当我们通过第二个构造函数创建一个新的MyClass时,我们实际上创建了两个ArrayList。其中一个马上被废弃了,因为变量的初始化是先于类构造函数执行的。类构造函数又创建了第二个ArrayList。对于编译器来说,我们的代码就好想是下面这样:
{
private ArrayList _coll;
MyClass()
{
_coll = new ArrayList();
}
MyClass(int size)
{
_coll = new ArrayList();
_coll = new ArrayList(size);
}
}
最后一种让我们将初始化工作放入构造函数中的原因是它便于异常处理。如果我们直接在声明时初始化,那么我们就不能将其放入try程序块中。如果我们要初始化的成员变量容易产生异常,那么我们应当将它放到构造函数中进行初始化,这将使得我们更容易捕获创建时产生的异常。
在声明时初始化成员变量是一种确保成员变量没有在构造函数中被遗漏初始化的简单方法。它会在构造函数运行之前被执行。当我们为类型使用了新的成员变量时,我们也不会因为忘记在构造函数中添加适当的初始化而犯错。在所有构造函数创建的成员变量相同的情况下,我们应当尽量使用即容易理解又容易维护的声明时初始化的方法。
译自 Effective C#:50 Specific Ways to Improve Your C# Bill Wagner著
回到目录