在声明
上初始化类成员变量是否更好?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;
}
这只是一种风格问题还是有某种方式权衡取舍?
答案 0 :(得分:75)
在表现方面,没有真正的区别;字段初始值设定项实现为构造函数逻辑。唯一的区别是字段初始化器发生在任何“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(...)
的构造函数中直接初始化 - 即{{ 1}} ctor。另一个构造函数不运行字段初始值设定项,因为它知道它们是由public Bar(string foo)
ctor完成的。
答案 1 :(得分:9)
实际上,您演示的字段初始化程序是一种方便的简写。编译器实际上将初始化代码复制到您为类型定义的每个实例构造函数的开头。
这有两个含义:第一,任何字段初始化代码都在每个构造函数中重复,第二,您在构造函数中包含的用于将字段初始化为特定值的任何代码实际上都会重新分配字段。
因此在性能方面,关于编译的代码大小,最好将字段初始化器移动到构造函数中。
另一方面,性能影响和代码“膨胀”通常是可以忽略的,字段初始化程序语法具有降低您可能忘记在其中一个构造函数中初始化某些字段的风险的重要好处。
答案 2 :(得分:4)
字段初始值设定项的一个主要限制是无法将它们包装在try-finally块中。如果在字段初始值设定项中抛出异常,则将放弃在先前初始值设定项中分配的任何资源;没有办法阻止它。如果笨拙地通过让受保护的基础构造函数通过引用接受IDisposable并将其指向自身作为其第一个操作,则可以处理构造中的其他错误。然后可以避免调用构造函数,除非通过工厂方法,在异常情况下将调用部分创建的对象上的Dispose。如果主要类构造函数在“走私”对新对象的引用后失败,则此保护将允许清除在派生类初始值设定项中创建的IDisposable。不幸的是,如果字段初始化程序失败,则无法提供此类保护。
答案 3 :(得分:2)
使用字段初始值设定项或创建Init()函数。将这些内容放在构造函数中的问题是,如果您需要添加第二个构造函数,最终会得到复制/粘贴代码(或者您忽略它并最终得到未初始化的变量)。
我要初始化声明的地方。或者让构造函数调用Init()函数。
答案 4 :(得分:1)
对于实例变量,它主要是样式问题(我更喜欢使用构造函数)。对于静态变量,有performance benefit初始化内联(当然不总是可行)。
答案 5 :(得分:0)
这完全取决于你。
我经常将它们内联初始化,因为当我不需要构造函数时,我不喜欢构造函数(我喜欢小类!)。
答案 6 :(得分:0)
关于上面的补充要点 - 在实现具有实现的类时,总是有一个构造函数。如果你没有声明一个,那么编译器[public Foo(){}]会推断出默认的指导者;一个不带参数的构造函数。
我经常喜欢提供这两种方法。允许那些希望使用它们的构造函数,并允许Field Initializers用于您希望使用类/类型的简化或默认实现的情况。这为您的代码增加了灵活性。请记住,如果他们选择,任何人都可以使用默认字段初始值设定项...如果您提供多个构造函数,请务必手动声明它 - public Foo(){}