102

在声明时初始化类成员变量是否更好

private List<Thing> _things = new List<Thing>();
private int _arb = 99;

还是在默认构造函数中?

private List<Thing> _things;
private int _arb;

public TheClass()
{
  _things = new List<Thing>();
  _arb = 99;
}

这仅仅是风格问题还是存在性能权衡,一种或另一种方式?

4

7 回答 7

84

在性能方面,没有真正的区别;字段初始值设定项被实现为构造函数逻辑。唯一的区别是字段初始值设定项发生在任何“base”/“this”构造函数之前。

构造函数方法可以与自动实现的属性一起使用(字段初始值设定项不能) - 即

[DefaultValue("")]
public string Foo {get;set;}
public Bar() { // ctor
  Foo = "";
}

除此之外,我更喜欢字段初始值设定项语法;我发现它可以使事情本地化-即

private readonly List<SomeClass> items = new List<SomeClass>();
public List<SomeClass> Items {get {return items;}}

我不必四处寻找它被分配的位置......

明显的例外是您需要执行复杂的逻辑或处理构造函数参数 - 在这种情况下,基于构造函数的初始化是要走的路。同样,如果您有多个构造函数,则最好始终以相同的方式设置字段 - 因此您可能有如下 ctors:

public Bar() : this("") {}
public Bar(string foo) {Foo = foo;}

编辑:作为旁注,请注意,在上面,如果有其他字段(未显示)具有字段初始化器,那么它们仅在调用的构造函数中直接初始化base(...)- 即public Bar(string foo)ctor。另一个构造函数运行字段初始值设定项,因为它知道它们是由this(...)ctor 完成的。

于 2008-11-18T09:04:34.293 回答
13

实际上,您演示的字段初始值设定项是一种方便的速记。编译器实际上将初始化代码复制到您为类型定义的每个实例构造函数的开头。

这有两个含义:首先,任何字段初始化代码在每个构造函数中都是重复的,其次,您在构造函数中包含的用于将字段初始化为特定值的任何代码实际上都会重新分配字段。

所以在性能方面,关于编译的代码大小,你最好将字段初始化器移动到构造函数中。

另一方面,性能影响和代码“膨胀”通常可以忽略不计,并且字段初始化语法具有降低您可能忘记在其中一个构造函数中初始化某些字段的风险的重要好处。

于 2008-11-18T09:12:22.670 回答
6

字段初始值设定项的一个主要限制是无法将它们包装在 try-finally 块中。如果在字段初始化器中抛出异常,则在之前的初始化器中分配的任何资源都将被放弃;没有办法阻止它。构造中的其他错误可以通过让受保护的基础构造函数通过引用接受 IDisposable 并将其指向自身作为其第一个操作来处​​理,如果笨拙的话。然后可以避免调用构造函数,除非通过工厂方法,如果发生异常,工厂方法将在部分创建的对象上调用 Dispose。如果主类构造函数在“走私”对新对象的引用后失败,则此保护将允许清理在派生类初始化程序中创建的 IDisposables。不幸的是,有'

于 2011-03-18T17:22:37.073 回答
3

例如变量,主要是风格问题(我更喜欢使用构造函数)。对于静态变量,初始化内联有性能优势(当然,并非总是可能)。

于 2008-11-18T08:58:17.643 回答
3

使用字段初始值设定项或创建 Init() 函数。将这些东西放在构造函数中的问题在于,如果您需要添加第二个构造函数,最终会得到复制/粘贴代码(或者忽略它并最终得到未初始化的变量)。

我要么在声明的地方初始化。或者让构造函数调用 Init() 函数。

于 2008-11-18T09:45:15.583 回答
1

这真的取决于你。
我经常内联初始化它们,因为当我真的不需要构造函数时我不喜欢有一个构造函数(我喜欢小类!)。

于 2008-11-18T09:04:24.387 回答
0

On added point to the above - You always have a constructor when implementing classes that have an implementation. If you do not declare one then the default instructor is inferred by the compiler [public Foo(){}]; a constructor that takes no arguments.

Often times I like to offer both approaches. Allow constructors for those that wish to use them and allow the Field Initializers for situations where you wish to use a simplified or default implementation of your class / type. This adds flexibility to your code. Keep in mind that anyone can use the default field initializer if they choose ... be sure to declare it manually if you offer more than one constructor - public Foo(){}

于 2012-02-06T11:46:58.190 回答