混合构造函数集字段和字段初始化符

问题描述:

设计类时,是否有关于初始化类字段的指导?有两种独立的初始化方法。我可以使用内联字段初始值设定项,或者我可以在构造函数中初始化它们。它变得更加有趣,当引入静态字段时,字段可以在定义时初始化,在静态构造函数中或在实例构造函数中进行检查,以查看字段是否已经设置。当引入工厂时,这变得更加混乱。混合构造函数集字段和字段初始化符

实施例1 - 内联动初始化


    public class ExampleOne 
    { 
     private readonly IDictionary _collection = new Dictionary(); 

     ... 
    }  

实施例2 - 构造函数初始化


    public class ExampleTwo 
    { 
     private readonly IDictionary _collection; 

     public ExampleTwo() 
     { 
      _collection = new Dictionary(); 
     } 
     ... 
    }  

实施例3 - 静态内嵌初始化


    public class ExampleThree 
    { 
     private static readonly IDictionary __collection = new Dictionary(); 

     ... 
    }  

实施例4 - 静态构造初始化


    public class ExampleFour 
    { 
     private static IDictionary __collection; 

     static ExampleFour() 
     { 
      _collection = new Dictionary(); 
     } 

     ... 
    }  

实施例5 - 静态/实例构造混合初始化


    public class ExampleFive 
    { 
     private static readonly IDictionary __collection; 
     private static IDictionary __anotherCollection; 

     static ExampleFive() 
     { 
      _collection = new Dictionary(); 
     } 

     public ExampleFive() 
     { 
      if(__anotherCollection == null) 
      { 
      __anotherCollection = new Dictionary(); 
      } 
     } 

     ... 
    }  

实施例6 - 工厂方法


    public class ExampleSix 
    { 
     private static readonly IDictionary __collection; 
     private static IDictionary __anotherCollection; 

     static ExampleSix() 
     { 
      _collection = new Dictionary(); 
     } 

     public static ExampleSix Create() 
     { 
      if(__anotherCollection == null) 
      { 
      __anotherCollection = new Dictionary(); 
      } 

      var example = new ExampleSix(); 
      return example; 
     } 

     ... 
    }  

目前我已经倾向于混合所有这些类。尽管我试图避免在实例构造函数中设置静态字段。

实施例7 - 混合


    public class ExampleSeven 
    { 
     private static readonly IDictionary __collection = new Dictionary(); 
     private static readonly IDictionary __anotherCollection; 
     private static readonly IDictionary __thirdCollection; 
     private static IDictionary __fourthCollection; 

     static ExampleSeven() 
     { 
      __anotherCollection = new Dictionary(); 
     } 

     public ExampleSeven() 
     { 
      if(__thirdCollection == null) 
      { 
       __thirdCollection = new Dictionary(); 
      } 
     } 

     public static ExampleSeven Create() 
     { 
      if(__fourthCollection == null) 
      { 
      __fourthCollection = new Dictionary(); 
      } 

      var example = new ExampleSeven(); 
      return example; 
     } 

     ... 
    }  

我更关注,如在实施例中可以看出,随着那些类对象,而不是简单的原语的字段。我明白,声明域时初始化将标记类beforeinit,事实上,我不能将this传递到声明时初始化的字段。我关心的主要是类似于上述七个例子的情况。我是否通过这种混合方式来解决任何不可预见的问题?

+4

这真的很难理解问题在这里。 *是否有问题? – Jon 2013-02-25 16:16:13

+1

这听起来像是你为自己设定的伤害世界。特别是如果你打算进行子类化,初始化的顺序有时候可能会第一眼看起来不直观。 (实例构造函数中的静态字典初始化看起来很有趣)我强烈建议您阅读Eric Lippert最近的[博客系列](http://ericlippert.com/2013/02/06/static-constructors-part-one/)话题。我想知道,在重新设计一些结构时,可能有一种更简单的方法来解决你正在做的事情。 – 2013-02-25 16:18:12

+1

@ChrisSinclair第三部分的结尾令人不安,因为Eric错了,Jon Skeet不得不纠正他在静态字段初始化程序运行时的情况。 – 2013-02-25 16:35:27

通常,初始化构造函数中的字段或使用字段初始值设定项并不重要。不过,也有一些细微之处,你应该知道有关操作的时机和顺序的:

  • 实例字段初始化和实例构造函数运行下面的命令,这可能会令人吃惊:

    • 派生类字段初始
    • 基类字段初始
    • 基类构造
    • 派生类的构造

请参阅本文由埃里克利珀对这种行为的原因的讨论:http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

要回答你的问题,有关于你应该初始化领域没有明确的规定。有时,如果初始化很短,初始化“内联”字段更自然,但如果它很长,它应该可能在构造函数中。有时字段要在构造函数中初始化,因为字段初始值设定项不能包含对this的引用。我的代码中经常会混合使用这两种风格,到目前为止,我并没有因此而遇到问题。请记住上面提到的执行顺序...

这个问题不只有一个答案。相反,这取决于你想在该领域存储什么类型的东西以及你想要做什么。

首先,问问自己:ExampleX绝对需要这个领域的对象吗?绝对是否所有用于ExampleX的用例都涉及该字段?如果不是,那么它不应该在构造函数中创建。如果它只用于一种方法,则应该在该方法中创建(或传递)该方法。如果它用于两个以上的方法,那么这些应该可能在他们自己的类中,即将类分成两个,一个依赖于该字段中的对象,另一个不依赖。其次,每次创建ExampleX时,你真的需要一个新的词典吗?让我们改变例子 - 而不是字典,它是某种提供服务的对象,设置起来很昂贵,但易于重用。在这种情况下,你应该明确地通过它而不是创建它 - 如果你创建一个新的,你正在浪费资源,并且使你的程序更加复杂和不可读,因为你引入了隐藏的依赖关系。通过传入对象,可以在构造函数签名(或方法签名,如果将它传递给方法)中通告依赖项。更不用说让它不可测或难以测试(就单元测试而言)。见http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/

所以,总结一下 - 你总是需要它吗?这告诉你是否在构造函数或其他地方创建/传入对象。它是否必须是新的?这将告诉你是否创建它或将其传入。

传入内容还会使代码更加灵活。见http://www.kevinwilliampang.com/2009/11/07/dependency-injection-for-dummies/