C#: 构造函数调用顺序

9

请看下面的代码:

代码

public class RecursiveConstructor
{
   //When this constructor is called 
   public RecursiveConstructor():this(One(), Two())
   {
       Console.WriteLine("Constructor one. Basic.");
   }

   public RecursiveConstructor(int i, int j)
   {
       Console.WriteLine("Constructor two.");
       Console.WriteLine("Total = " + (i+j));
   }

   public static int One()
   {
       return 1;
   }

   public static int Two()
   {
       return 2;
   }
}

调用方法

public class RecursiveConstructorTest
{
    public static void Main()
    {
        RecursiveConstructor recursiveConstructor = new RecursiveConstructor();

        Console.ReadKey();
    }
}

结果

第二个构造函数。

总数=3

第一个构造函数。基本的。

为什么第二个构造函数先运行?

我知道在链式构造函数中我们首先调用基类构造函数,然后再往上回溯,但是当构造函数保存在同一类中时,为什么我们仍然看到这种行为,额外的构造函数会先被调用?

我原本以为最基本的构造函数内容会首先执行。


11
顺便说一下,这不是递归的。 - Ian Newson
我认为这仍然是构造函数链。 - mbm
谢谢。我错误地认为这是一个递归构造函数。我已批准标题的编辑。所以我们在这里看到的本质上是构造函数链,但在同一个类内部而不是跨继承和基类之间? - CSharpened
1
我并不完全清楚你的问题 - "为什么第二个构造函数会先运行?" - 因为你已经要求第二个构造函数先运行了!如果你想要不同的行为,你需要编写不同的代码。 - AakashM
4个回答

7
我认为编译器运行更安全的场景。 如果在此处调用另一个构造函数,则有可能该构造函数是您当前构造函数的先决条件。这种行为与调用基础构造函数时公开的行为一致,因此可以预期。 创建类的新实例时,会从最不专业的(对象类的构造函数)到最专业的(当前类的构造函数)调用一系列构造函数。 操作符“:”允许您在此链中显式添加构造函数,因此这个顺序看起来很自然。

即使您没有明确编写它,也会在类的构造函数之前始终调用默认的 base() 构造函数,因此附加 : this(..) 也将首先被调用。这与已经隐含存在的行为一致。 - mtijn
1
是的,这是一个依赖链。: 操作符被用来显式地将元素添加到此依赖链中。 - dureuill

2

你已经自己给出了解释。这与调用基础构造函数的方式几乎相同。每当在你的签名中调用一个构造函数,比如

 public RecursiveConstructor() : this(One(), Two())

或者

 public RecursiveConstructor() : base()
< p >在< code >:后面的构造函数首先被调用。


这个框架中为什么要以这种方式编码? - CSharpened
1
“原始的”无参数构造函数可以(例如)初始化一些属性或字段(其值可以在参数化构造函数中设置,而不会抛出空引用异常)。 - Konrad Morawski
@CSharpened 我无法解释为什么代码是这样编写的。内部实现可能会在“:”后立即调用构造函数,就像在Java中一样。在那里,通常会在签名后的第一个关键字处调用“super()”或“this()”。 - oopbase
1
@CSharpened,我猜您想要能够从默认构造函数调用更具体的构造函数,以便能够传递默认值。使用“:`”调用它还会执行所有其他实例化操作,因此在使用更一般的设置之前,您知道类首先被构造。当然,如果您希望这样做,您也可以在默认构造函数中调用更具体的构造函数。 - Nebula

2

考虑到初始化新对象时始终存在构造函数调用的分层链,这个做法是有道理的。正如您所说,首先调用基类的构造函数。

构造函数初始化程序的两种形式: base(...): this(...)的行为方式相同,通常情况下会隐式调用。

因此,在您的情况下,我们有一个链:

Object()

然后...

RecursiveConstructor(int i, int j)

然后...

RecursiveConstructor()

1

它首先调用构造函数1,但是在它到达ctor1代码块之前,ctor1会调用ctor2,因此您看到的输出。

解决这个问题的一种方法,但保留DRY行为的方法是重构(int,int)重载:

   //When this constructor is called 
   public RecursiveConstructor()
   {
       Console.WriteLine("Constructor one. Basic.");
       Init(One(), Two());
   }

   public RecursiveConstructor(int i, int j)
   {
       Console.WriteLine("Ctor 2");
       Init(i, j);
   }


   private void Init(int i, int j)
   {
       Console.WriteLine("Refactored");
       Console.WriteLine("Total = " + (i+j));
   }

有趣的是,这种方式链接构造函数通常被称为“委托构造函数”。

在Java中,可以在代码块中调用其他构造函数(例如在此处查看),但它必须是块中的第一行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接