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

时间:2009-07-21 03:45:49

标签: c# constructor initialization field

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

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中一样直接初始化一个字段?

7 个答案:

答案 0 :(得分:5)

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

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

请参阅以下示例以查看差异(您可以将&amp; past复制到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();
        }
    }
}

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

答案 1 :(得分:3)

有一点不同:

  

字段立即初始化   在对象的构造函数之前   实例被调用,所以如果   构造函数赋值的值   字段,它将覆盖任何值   在实地申报期间给出。 From MSDN

答案 2 :(得分:3)

行为方面两者应该相同。
但是,您可能需要考虑的是边缘情况 IL - Bloat 。字段初始值设定项的IL插入到每个ctor的顶部。由此可见,如果您有许多字段初始值设定项许多超载ctors ,则IL的相同部分会以ctor重载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 :(得分:2)

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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