看完网络研讨会Jon Skeet Inspects ReSharper之后,我开始对递归构造函数调用进行一些研究,发现以下代码是有效的C#代码(有效,我是说它可以编译)。
class Foo
{
int a = null;
int b = AppDomain.CurrentDomain;
int c = "string to int";
int d = NonExistingMethod();
int e = Invalid<Method>Name<<Indeeed();
Foo() :this(0) { }
Foo(int v) :this() { }
}
众所周知,字段初始化由编译器移入构造函数。因此,如果您有一个类似的字段int a = 42;,那么您将a = 42在所有构造函数中使用。但是,如果您有构造函数调用另一个构造函数,则只有被调用的构造函数中会有初始化代码。
例如,如果您的构造函数的参数调用默认构造函数,则a = 42仅在默认构造函数中进行赋值。
为了说明第二种情况,下面的代码:
class Foo
{
int a = 42;
Foo() :this(60) { }
Foo(int v) { }
}
编译成:
internal class Foo
{
private int a;
private Foo()
{
this.ctor(60);
}
private Foo(int v)
{
this.a = 42;
base.ctor();
}
}
因此,主要的问题是,在此问题开始时给出的代码被编译为:
internal class Foo
{
private int a;
private int b;
private int c;
private int d;
private int e;
private Foo()
{
this.ctor(0);
}
private Foo(int v)
{
this.ctor();
}
}
如您所见,编译器无法决定将字段初始化放在何处,因此,它也不会放在任何地方。另请注意,没有base构造函数调用。当然,无法创建任何对象,StackOverflowException如果您尝试创建的实例,那么您将最终会遇到麻烦Foo。
我有两个问题:
为什么编译器完全允许递归构造函数调用?
为什么我们观察到在此类中初始化的字段的编译器行为?
注意事项:ReSharper会警告您Possible cyclic constructor calls。此外,在Java中,此类构造函数调用不会进行事件编译,因此Java编译器在这种情况下更具限制性(Jon在网络研讨会上提到了此信息)。
这使这些问题变得更加有趣,因为就Java社区而言,C#编译器至少更现代。