【问题标题】:C# constructor execution orderC#构造函数执行顺序
【发布时间】:2010-12-25 08:35:37
【问题描述】:

在 C# 中,当你这样做时

Class(Type param1, Type param2) : base(param1) 

是先执行类的构造函数,然后调用超类构造函数还是先调用基构造函数?

【问题讨论】:

  • 我从未使用过 XNA,但我没有在您的...问题中看到问题。
  • 是这样吗?这就是你给我们的一切吗?来吧,给我们一个线索......动物,蔬菜,矿物?
  • 我按得太快了xD...对不起,我还在写...

标签: c# inheritance constructor constructor-chaining


【解决方案1】:

顺序是:

  • 成员变量被初始化为层次结构中所有类的默认值

然后从派生最多的类开始:

  • 为派生最多的类型执行变量初始化器
  • 构造函数链接确定将调用哪个基类构造函数
  • 基类已初始化(递归所有这些:)
  • 执行该类中链中的构造函数主体(请注意,如果它们与 Foo() : this(...) 等链接,则可以有多个

请注意,在 Java 中,基类在变量初始化程序运行之前被初始化。如果你曾经移植过任何代码,这是一个重要的区别:)

如果你有兴趣,我有一个page with more details

【讨论】:

  • 我在这里的贡献似乎有一半指向他的答案;但是 Eric Lippert 也写了一篇关于这个主题的好帖子:blogs.msdn.com/ericlippert/archive/2008/02/15/…blogs.msdn.com/ericlippert/archive/2008/02/18/…
  • +1 用于与 Java 的比较。另外,我认为 C++ 就是这种情况,但我不是 100% 确定。
  • 仅供参考,在 vb.net 中,派生类字段初始化程序在基类初始化完成后运行(恕我直言,事情应该是这样)。除此之外,这意味着派生类字段可以使用正在构建的对象的字段或属性进行初始化。
  • @supercat:另一方面,这意味着你不能调用基类中的任何成员,因为那部分还没有被初始化......
  • @Jon Skeet:在 vb.net 中,您可以从派生类初始化程序中调用基类成员。从基类构造函数虚拟调用的派生类方法不能依赖于它们的字段初始值设定项来运行,但我认为依赖派生类字段初始值设定项在基构造函数之前运行的设计无论如何都有些脆弱,因为它们会不适用于初始化值取决于构造参数的任何场景。
【解决方案2】:

它将首先调用基本构造函数。还要记住,如果你不把:base(param1)放在你的构造函数之后,就会调用base的空构造函数。

【讨论】:

    【解决方案3】:

    首先调用基类的构造函数。

    【讨论】:

      【解决方案4】:

      不确定这是否应该是评论/答案,但对于那些通过示例学习的人来说,这个小提琴也说明了顺序:https://dotnetfiddle.net/kETPKP

      using System;
      
      // order is approximately
      /*
         1) most derived initializers first.
         2) most base constructors first (or top-level in constructor-stack first.)
      */
      public class Program
      {
          public static void Main()
          {
              var d = new D();
          }
      }
      
      public class A
      {
          public readonly C ac = new C("A");
      
          public A()
          {
              Console.WriteLine("A");
          }
          public A(string x) : this()
          {
              Console.WriteLine("A got " + x);
          }
      }
      
      public class B : A
      {
          public readonly C bc = new C("B");
      
          public B(): base()
          {
              Console.WriteLine("B");
          }
          public B(string x): base(x)
          {
              Console.WriteLine("B got " + x);
          }
      }
      
      public class D : B
      {
          public readonly C dc = new C("D");
      
          public D(): this("ha")
          {
              Console.WriteLine("D");
          }
          public D(string x) : base(x)
          {
              Console.WriteLine("D got " + x);
          }
      }
      
      public class C
      {
          public C(string caller)
          {
              Console.WriteLine(caller + "'s C.");
          }
      }
      

      结果:

      D's C.
      B's C.
      A's C.
      A
      A got ha
      B got ha
      D got ha
      D
      

      【讨论】:

        【解决方案5】:

        巧合的是,这也是一个被问到最多的面试问题,在youtube video with demonstration 中有解释。

        首先不要试图记住顺序。 在父子关系中逻辑思考,子建立在父子之上。所以显然应该创建第一个 Parent 实例,然后创建子实例。

        所以首先触发父构造函数,然后触发子构造函数。

        但是,当涉及到初始化器时,情况有所不同。

        对于初始化程序,首先运行子初始化程序,然后运行父初始化程序。

        下面是相同的图解表示。所以构造函数代码将 Parent 触发到 Child 并且 Initializers 从 Child 触发到 Parent。

        【讨论】:

          【解决方案6】:

          [编辑:在我回答的时候,问题已经完全改变了]。

          答案是它先调用base。

          [下面老问题的原始答案]

          您是在问何时执行构造函数调用的“基础”位?

          如果是这样,如果该类派生自具有此构造函数的另一个类,则您将“链接”对构造函数基的调用:

            public class CollisionBase
              {
                  public CollisionBase(Body body, GameObject entity)
                  {
          
                  }
              }
          
              public class TerrainCollision : CollisionBase
              {
                  public TerrainCollision(Body body, GameObject entity)
                      : base(body, entity)
                  {
          
                  }
              }
          

          在此示例中,TerrainCollision 派生自 CollisionBase。通过以这种方式链接构造函数,它确保使用提供的参数在基类上调用指定的构造函数,而不是默认构造函数(如果基类上有一个)

          【讨论】:

            【解决方案7】:

            您的问题有点不清楚,但我假设您打算问以下问题

            何时调用 XNA 对象的基本构造函数与使用隐含的默认构造函数

            这个问题的答案在很大程度上取决于您的场景和底层对象。你能澄清一下以下内容吗

            • 场景是什么
            • TerrainCollision 的基础对象的类型是什么?

            不过,我最好的答案是,如果您的参数与基类构造函数的参数一致,那么您几乎肯定应该调用它。

            【讨论】:

              【解决方案8】:

              构造函数机制要好得多,因为它让应用程序使用构造函数链接,并且如果您要扩展应用程序,它通过继承实现了进行最小代码更改的能力。 Jon Skeets Article

              【讨论】:

                猜你喜欢
                • 2019-11-13
                • 2012-04-10
                • 1970-01-01
                • 2016-03-18
                • 2011-10-28
                • 2016-04-20
                • 2020-08-20
                • 2022-06-10
                • 2013-01-29
                相关资源
                最近更新 更多