【问题标题】:Field initialization字段初始化
【发布时间】:2010-12-27 14:19:04
【问题描述】:

以下两种字段初始化方式有区别吗?什么时候用哪个?

第一种方式

public class Class1
{
   private SomeClass someclass;

   public Class1()
   {
       someclass = new SomeClass(some arg);
   }
}

第二种方式

public class Class1
{
   private SomeClass someclass = new SomeClass(some arg);
}

第二个例子中的字段可以是只读的。

【问题讨论】:

    标签: c# .net oop


    【解决方案1】:

    推荐构造方法,因为异常管理和调试商品。

    如果一个字段应该是只读的,你可以声明一个只读属性(这个只有getter)。

    类的instance field variable initializers 对应于在进入该类的任何实例构造函数时立即执行的一系列赋值。变量初始化器按照它们在类声明中出现的文本顺序执行。

    实例字段的变量初始化器不能引用正在创建的实例。因此,在变量初始化程序中引用 this 是编译时错误,因为变量初始化程序通过简单名称引用任何实例成员是编译时错误。

    默认value initialization 出现在所有字段中,包括具有变量初始值设定项的字段。因此,当一个类被初始化时,该类中的所有静态字段首先被初始化为其默认值,然后静态字段初始化程序按文本顺序执行。同样,当创建一个类的实例时,该实例中的所有实例字段首先被初始化为其默认值,然后实例字段初始化器按文本顺序执行。

    【讨论】:

    • 不正确。编译器生成代码以在构造函数的开头执行内联初始化程序。您可以单步执行内联初始化程序。
    • 我删除了“没有区别”。而且我没有说你不能跨过这些线。
    • 不推荐构造方法。虽然您不能使用内联方法处理异常,但在调试方面没有区别。使用一种方法而不是另一种方法有不同的原因,但在一般情况下,它们是等效的,任何“建议”都纯粹是主观的。
    • 如果不能处理异常,为什么不应该“推荐”构造函数variansh。我想知道什么时候内联方法比构造方法更“推荐”。
    【解决方案2】:

    事实上,这两个类中的字段都可以是只读的。

    【讨论】:

      【解决方案3】:

      除了代码行数之外,还有细微的差别。

      例如,字段初始化发生在构造函数运行之前。在您的示例中没有太大区别,但需要牢记。

      我会将第二个示例中的字段初始化保留为简单的(字符串或整数),以避免在初始化期间发生可能的异常。

      如上所述,在这两种方式中,字段都可以是只读的,因为只读字段只能在构造期间写入。

      【讨论】:

        【解决方案4】:

        如果您的“some arg”参数不是静态的,第一个很有用。如果该参数只能通过构造函数获得,那么这就是采用的方法。 第二种方式有问题。如果在 SomeClass 的实例化过程中抛出异常,则无法在 Class1 中捕获该异常。

        最好的祝愿,
        费边

        【讨论】:

          【解决方案5】:

          有一个细微的区别,第二个示例中的字段将在基类中的字段初始化之前初始化,而第一个示例中的字段将在之后初始化。然而,这很少有任何影响。

          这在很大程度上是风格和偏好的问题。就我个人而言,我更喜欢第二种,因为它使构造函数更清晰,可以进行更多基于逻辑的初始化,但有充分的理由让所有初始化都在构造函数中完成。

          为了完整起见,初始化顺序如下:

          1. 静态字段
          2. 静态构造函数
          3. 实例字段
          4. 基础静态字段
          5. 基础静态构造函数
          6. 基本实例字段
          7. 基础构造函数
          8. 构造函数

          【讨论】:

          • 所以你实际上更喜欢第二种方式,而不是第一种:)。尽管有细微差别,但 +1。
          • 当基础构造函数没有被显式调用时,7->8 呢?
          • @serhio: 基本构造函数总是被调用,即使你没有明确地调用它。如果没有显式调用基构造函数,则在派生构造函数的开头调用默认(无参数)构造函数(因此,如果基类不提供公共或受保护的默认构造函数并且您提供了编译器错误的原因)不要告诉它该调用哪一个)。
          • 我认为在静态字段初始化器之前,默认值被分配给静态字段,因此静态字段初始化器可以在初始化之前引用其他静态字段。
          • @GhasanAl-Sakkaf 不,静态字段初始化程序按文本顺序执行:stackoverflow.com/a/3681278/616827
          【解决方案6】:

          差别很小。编译器会将所有内联初始化程序按照定义的顺序放置在构造函数的开头。

          如果您需要复杂的逻辑来初始化字段,您可能希望使用构造函数方法,否则我认为内联方法更清晰,更易于维护,因为编译器会为您处理调用它。

          【讨论】:

            【解决方案7】:

            其实我更喜欢第二个,因为可读性和调试方便,你可以用 try catch 包装调用,但第一个你不能。

            【讨论】:

              【解决方案8】:

              存在差异。

              想象你有一个有多个构造函数的类。使用第一种方式,每个构造函数都需要创建这些对象。这可能是预期的行为,因为您可能希望每次都以不同的方式创建对象。 构造函数的这种额外责任可能是一件坏事,因为如果你不记得在每个构造函数中初始化变量,你最终会得到一个空对象。

              需要考虑的效率很少,但意义不大 - 第一种方法需要两次赋值,首先是 null,然后是创建的对象,而第二种方法是一步创建和初始化对象。

              然后考虑静态变量。静态变量必须以第二种方式声明,因为永远无法保证您的类的实例会被创建。

              【讨论】:

              • 通常将构造函数链接在一起,以便在最通用的构造函数中具有初始化逻辑,然后每个构造函数执行其特定位,然后调用更通用的构造函数。这样可以避免代码重复。
              • 没错,但至少您可以选择将对象初始化为不同的子类或使用不同的参数。
              • 另外,静态字段只会在创建对象时初始化,无论您声明它们的方式,确定吗?
              • 每次创建类的新实例时重新初始化静态成员从逻辑的角度来看是没有意义的。
              【解决方案9】:

              内联初始化字段时不能使用this 关键字。原因是代码执行的顺序:出于所有意图和目的,初始化字段内联的代码在类的构造函数之前运行(即 C# 编译器将阻止访问 this 关键字) .基本上这意味着这不会编译:

              public class Class1
              {
                 private SomeClass someclass = new SomeClass(this);
              
                 public Class1()
                 {
                 }
              }
              

              但这会:

              public class Class1
              {
                 private SomeClass someclass;
              
                 public Class1()
                 {
                     someclass = new SomeClass(this);
                 }
              }
              

              这是一个微妙的区别,但值得一提。

              两个版本之间的其他差异只有在使用继承时才真正明显。如果你有两个相互继承的类,首先初始化派生类中的字段,然后初始化基类中的字段,然后调用基类的构造函数,最后调用构造函数派生类将被调用。在某些情况下,您需要对此非常小心,因为如果您没有意识到发生了什么,它可能会导致复杂的水果沙拉(其中一个涉及在基类构造函数中调用虚拟方法,但是几乎从来都不是明智之举)。举个例子:

              class BaseClass
              {
                  private readonly object objectA = new object(); // Second
                  private readonly object objectB;
              
                  public BaseClass()
                  {
                      this.objectB = new object(); // Third
                  }
              }
              
              class DerivedClass : BaseClass
              {
                  private object objectC = new object(); // First
                  private object objectD;
              
                  public DerivedClass()
                  {
                      this.objectD = new object(); // Forth
                  }
              }
              

              您需要在所有初始化字段的行上设置断点才能看到正确的序列。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-09-25
                • 2021-07-26
                • 2020-02-13
                • 2012-12-18
                • 2011-03-16
                相关资源
                最近更新 更多