C# 成员变量初始化; 最佳实践?
初始化更好
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;
}
? 这仅仅是一种风格问题还是存在性能权衡(某种方式)?
Is it better to initialize class member variables on declaration
private List<Thing> _things = new List<Thing>();
private int _arb = 99;
or in the default constructor?
private List<Thing> _things;
private int _arb;
public TheClass()
{
_things = new List<Thing>();
_arb = 99;
}
Is it simply a matter of style or are there performance trade-offs, one way or the other?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
从性能上来说,没有什么本质区别; 字段初始值设定项作为构造函数逻辑实现。 唯一的区别是字段初始值设定项发生在任何“base”/“this”构造函数之前。
构造函数方法可以与自动实现的属性一起使用(字段初始值设定项不能) - 即
除此之外,我倾向于更喜欢字段初始值设定项语法; 我发现它使事情保持本地化 - 即
我不必上下寻找它被分配的位置......
明显的例外是你需要执行复杂的逻辑或处理构造函数参数 - 在这种情况下构造函数 -基于初始化是要走的路。 同样,如果您有多个构造函数,那么字段最好总是以相同的方式设置 - 因此您可能有类似以下的构造函数:
编辑:作为旁注,请注意在上面,如果还有其他字段(不是显示)带有字段初始值设定项,那么它们仅在调用
base(...)
的构造函数中直接初始化 - 即public Bar(string foo)
构造函数。 另一个构造函数不运行字段初始值设定项,因为它知道它们是由this(...)
构造函数完成的。In terms of performance, there is no real difference; field initializers are implemented as constructor logic. The only difference is that field initializers happen before any "base"/"this" constructor.
The constructor approach can be used with auto-implemented properties (field initializers cannot) - i.e.
Other than that, I tend to prefer the field initializer syntax; I find it keeps things localized - i.e.
I don't have to go hunting up and down to find where it is assigned...
The obvious exception is where you need to perform complex logic or deal with constructor parameters - in which case constructor-based initialization is the way to go. Likewise, if you have multiple constructors, it would be preferable for the fields to always get set the same way - so you might have ctors like:
edit: as a side comment, note that in the above, if there are other fields (not shown) with field initializers, then they are only directly initialized in the constructors that call
base(...)
- i.e. thepublic Bar(string foo)
ctor. The other constructor does not run field initializers, since it knows they are done by thethis(...)
ctor.实际上,您演示的字段初始值设定项是一种方便的简写。 编译器实际上将初始化代码复制到您为类型定义的每个实例构造函数的开头。
这有两个含义:首先,任何字段初始化代码都会在每个构造函数中重复,其次,您在构造函数中包含的用于将字段初始化为特定值的任何代码实际上都会重新分配字段。
因此,就性能而言,以及编译代码大小方面,您最好将字段初始值设定项移至构造函数中。
另一方面,性能影响和代码“膨胀”通常可以忽略不计,并且字段初始值设定项语法具有重要的好处,可以减少您可能忘记在某个构造函数中初始化某些字段的风险。
Actually, field initializers as you demonstrate is a convenient shorthand. The compiler actually copies the initialization code into the beginning of each instance constructor that you define for your type.
This has two implications: first, any field initialization code is duplicated in each constructor and, second, any code you include in your constructors to initialize fields to specific values will in fact re-assign the fields.
So performance-wise, and with regards to compiled code size, you're better off moving field initializers into constructors.
On the other hand, the performance impact and code 'bloat' will usually be negligable, and the field initializer syntax has the important benefit of lessening the risk that you might forget to initialize some field in one of your constructors.
字段初始值设定项的一个主要限制是无法将它们包装在 try-finally 块中。 如果在字段初始值设定项中引发异常,则在先前初始值设定项中分配的任何资源都将被放弃; 没有办法阻止它。 构造中的其他错误可以通过让受保护的基构造函数通过引用接受 IDisposable 并将其指向自身作为其第一个操作来处理(如果笨拙的话)。 然后,我们可以避免调用构造函数,除非通过工厂方法,在发生异常的情况下,将在部分创建的对象上调用 Dispose。 如果主类构造函数在“走私”对新对象的引用后失败,则此保护将允许清理在派生类初始值设定项中创建的 IDisposables。 不幸的是,如果字段初始值设定项失败,则无法提供此类保护。
One major limitation with field initializers is that there's no way to wrap them in a try-finally block. If an exception is thrown in a field initializer, any resources that were allocated in previous initializers will be abandoned; there's no way to prevent it. Other errors in construction can be dealt with, if awkwardly, by having a protected base constructor accept an IDisposable by reference, and pointing it at itself as its very first operation. One can then avoid calling the constructor except through factory methods which in case of exception will call Dispose on the partially-created object. This protection will allow for cleanup of IDisposables created in derived-class initializers if the main-class constructor fails after "smuggling out" a reference to the new object. Unfortunately, there's no way to provide such protection if a field initializer fails.
使用字段初始值设定项或创建 Init() 函数。 将这些东西放入构造函数中的问题是,如果您需要添加第二个构造函数,您最终会得到复制/粘贴代码(或者您忽略它并最终得到未初始化的变量)。
我要么在声明的地方初始化。 或者让构造函数调用 Init() 函数。
Use either field initializers or create an Init() function. The problem with putting these things in your constructor is that if you ever need to add a 2nd constructor, you end up with copy/paste code (or you overlook it and end up with uninitialized variables).
I would either initialize where declared. Or have the constructor(s) call an Init() function.
对于实例变量,这很大程度上是风格问题(我更喜欢使用构造函数)。 对于静态变量,性能优势内联初始化(当然并不总是可能)。
For instance variables, it is largely a matter of style (I prefer using a constructor). For static variables, there is a performance benefit to initializing inline (not always possible, of course).
这真的取决于你。
我经常内联初始化它们,因为当我并不真正需要构造函数时,我不喜欢有构造函数(我喜欢小类!)。
It's really up to you.
I often initialize them inline, cause I don't like having a constructor when I don't really need one (I like small classes !).
上面的补充一点 - 在实现具有实现的类时,您总是有一个构造函数。 如果您没有声明,则编译器将推断默认的指导者 [public Foo(){}]; 不带参数的构造函数。
很多时候我喜欢提供这两种方法。 允许那些希望使用它们的构造函数,并允许在您希望使用类/类型的简化或默认实现的情况下使用字段初始值设定项。 这增加了代码的灵活性。 请记住,任何人都可以使用默认字段初始值设定项(如果他们选择)...如果您提供多个构造函数,请务必手动声明它 - public Foo(){}
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(){}