在观看网络研讨会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# 编译器至少更现代。