【问题标题】:Initializing Class Fields at the Field Definition or in Class Constructor在字段定义或类构造函数中初始化类字段
【发布时间】:2009-07-21 03:45:48
【问题描述】:

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

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 那样直接初始化一个字段更好吗?

【问题讨论】:

  • 他们已经有默认值了……不需要再初始化了。

标签: c# constructor initialization field


【解决方案1】:

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

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

查看以下示例以查看差异(您可以复制并粘贴到 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();
        }
    }
}

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

【讨论】:

    【解决方案2】:

    有一个区别:

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

    【讨论】:

      【解决方案3】:

      在行为方面两者应该相同。
      但是,您可能需要考虑一个极端情况 IL - Bloat。字段初始化器的 IL 被插入到每个 ctor 的顶部。由此可见,如果你有许多字段初始化器许多重载的ctors,IL 的同一部分将作为ctor 重载IL 的前缀。因此,与使用构造函数链接或委托给公共 Initialize() 函数(其中重复的 IL 将是方法调用)的情况相比,程序集的总大小可能会增加。因此对于这种特定场景,字段初始化器将是一个相对较弱的选择。

      您可以使用反射器在此代码 sn-p 的二进制文件上验证这一点

      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
         }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

            • 最初创建该类的开发人员应该使用正确的构造函数链,所以它不应该是一个问题。但是,一年后修改该类的程序员在接触该类之前会更好地理解它。他应该确保所有字段都被正确初始化。
            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 2019-01-21
              • 1970-01-01
              • 1970-01-01
              • 2015-03-30
              • 1970-01-01
              • 1970-01-01
              • 2016-02-22
              • 2018-05-10
              • 1970-01-01
              相关资源
              最近更新 更多