在字段定义或类构造函数中初始化类字段

我有一个类,其中一个字段需要在初始化对象时进行初始化,例如需要在对象添加/删除之前创建的列表。

public class MyClass1 { private List _otherClassList; public MyClass1() { this._otherClasslist = new List(); } } public class MyClass2 { private List = new List(); public MyClass2() { } } 

这两个类有什么区别,为什么你会选择一种方法而不是另一种方法呢?

我通常在构造函数中设置字段,就像在MyClass1中一样,因为我发现更容易在一个地方查看以查看实例化对象时发生的所有事情,但是在任何情况下都更好像在MyClass2中一样直接初始化一个字段?

在两种情况下(即使在Debug和Release版本中),C#编译器(VS2008 sp1)发出的IL几乎都是等效的。

但是,如果需要添加以 List 作为 参数的参数化构造 函数 ,它将会有所不同(特别是,当您使用此类构造函数创建大量对象时)。

请参阅以下示例以查看差异(您可以复制和过去到VS并构建它以查看带有Reflector或ILDASM的IL )。

 using System; using System.Collections.Generic; namespace Ctors { //Tested with VS2008 SP1 class A { //This will be executed before entering any constructor bodies... private List myList = new List(); public A() { } //This will create an unused temp List object //in both Debug and Release build public A(List list) { myList = list; } } class B { private List myList; //ILs emitted by C# compiler are identicial to //those of public A() in both Debug and Release build public B() { myList = new List(); } //No garbage here public B(List list) { myList = list; } } class C { private List myList = null; //In Release build, this is identical to B(), //In Debug build, ILs to initialize myList to null is inserted. //So more ILs than B() in Debug build. public C() { myList = new List(); } //This is identical to B(List list) //in both Debug and Release build. public C(List list) { myList = list; } } class D { //This will be executed before entering a try/catch block //in the default constructor private E myE = new E(); public D() { try { } catch (NotImplementedException e) { //Cannot catch NotImplementedException thrown by E(). Console.WriteLine("Can I catch here???"); } } } public class E { public E() { throw new NotImplementedException(); } } class Program { static void Main(string[] args) { //This will result in an unhandled exception. //You may want to use try/catch block when constructing D objects. D myD = new D(); } } } 

注意:切换到Release版本时,我没有更改任何优化标志。

有一个区别:

字段在调用对象实例的构造函数之前立即初始化,因此如果构造函数指定字段的值,它将覆盖字段声明期间给出的任何值。 来自MSDN :

行为方面两者应该是相同的。
然而,你可能想要考虑的是边缘情况IL – Bloat 。 字段初始值设定项的IL插入到每个ctor的顶部。 从那以后,如果你有许多字段初始化器许多过载的ctors ,IL的相同部分以ctor重载IL为前缀。 因此,与使用构造函数链接或委托给公共Initialize()函数(其中重复的IL将是方法调用)的情况相比,程序集的总大小可能会增加。 因此,对于这种特定情况,字段初始化器将是一个相对较弱的选择。

您可以使用二进制文件中的reflection器对此代码段进行validation

 public class MyClass2 { private List whack = new List(); // lots of other field initializers public MyClass2() { Console.WriteLine("Default ctor"); } public MyClass2(string s) { Console.WriteLine("String overload"); } // lots of other overload ctors } 

在构造函数中初始化时,我认为如果有必要,捕获和处理exception会更容易。

在MyClass1中,有人可能会覆盖构造函数并导致问题。

代码是等价的,因为编译器在第二种情况下在每个构造函数中放置初始化,第二种情况的好处是程序员在编写该类之后将添加新构造函数时不会想到初始化字段:)

在c#中,您的两个示例之间几乎没有区别。 但是,开发人员倾向于在构造函数中初始化他们的字段,因为它不容易出错。