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

9

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

public class MyClass1
{
    private List<MyOtherClass> _otherClassList;

    public MyClass1()
    {
        this._otherClasslist = new List<MyOtherClass>();
    }
}


public class MyClass2
{
    private List<MyOtherClass> = new List<MyOtherClass>();

    public MyClass2()
    {
    }
}

这两个类之间有什么区别,为什么会选择其中一种方法?
通常我会在构造函数中设置该字段,就像MyClass1一样,因为我发现只需要查看一个地方即可了解对象实例化时发生的所有事情,但有没有任何情况更好的直接初始化字段,就像MyClass2一样?

它们已经有默认值了... 不需要再次初始化它们。 - Yousha Aleayoub
7个回答

5

C#编译器(VS2008 sp1)生成的IL对于这两种情况几乎是相同的(即使在Debug和Release构建中也是如此)。

然而,如果您需要添加以List<MyOtherClass>作为参数的参数化构造函数,则会有所不同(特别是当您使用这样的构造函数创建大量对象时)。

请参阅以下示例以查看差异(您可以将其复制并粘贴到VS中进行构建,然后使用ReflectorILDASM查看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<string> myList = new List<string>();

        public A() { }

        //This will create an unused temp List<string> object 
        //in both Debug and Release build
        public A(List<string> list)
        {
            myList = list;
        }
    }

    class B
    {
        private List<string> myList;

        //ILs emitted by C# compiler are identicial to 
        //those of public A() in both Debug and Release build 
        public B()
        {
            myList = new List<string>();
        }

        //No garbage here
        public B(List<string> list)
        {
            myList = list;
        }
    }

    class C
    {

        private List<string> 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<string>();
        }

        //This is identical to B(List<string> list) 
        //in both Debug and Release build. 
        public C(List<string> 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();
        }
    }
}

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


3
行为上两者应该是相同的。 然而,您可能需要考虑一个边缘情况 IL - Bloat 。字段初始化程序的IL被插入到每个构造函数的顶部。如果您有许多字段初始化程序和许多重载的构造函数,则相同的IL段将作为前缀添加到构造函数重载IL中。因此,与使用构造函数链接或委托到常见的Initialize()函数(其中重复的IL将是方法调用)相比,您的程序集的总大小可能会增加。因此,在这种特定情况下,字段初始化程序将是一个相对较弱的选择。 您可以在此代码片段的二进制文件上使用反编译器进行验证。
public class MyClass2
   {
    private List<int> whack = new List<int>();
    // lots of other field initializers

    public MyClass2()
    {
       Console.WriteLine("Default ctor");
    }
    public MyClass2(string s)
    {
       Console.WriteLine("String overload");
    }
      // lots of other overload ctors
   }

3

有一个区别:

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


2
在构造函数中进行初始化时,如果需要捕获和处理异常,我认为这将更容易。

1
在 MyClass1 中,某人可能会覆盖构造函数并引起问题。

0
代码是等效的,因为编译器在第二种情况下会在每个构造函数中进行初始化。第二种情况的好处是,当程序员在写了一年那个类之后添加新的构造函数时,他不需要考虑初始化字段的问题 :)

最初创建类的开发人员应该使用适当的构造函数链接,这样就不会有问题。然而,一年后修改该类的程序员最好在操作之前了解该类。他应该确保所有字段都被正确初始化。 - Bobby Cannon

0
在C#中,你的这两个例子实际上没有什么区别。但是,开发人员倾向于在构造函数中初始化它们的字段,因为这样更不容易出错。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接