【发布时间】:2013-05-14 18:14:30
【问题描述】:
看完网络研讨会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; 这样的字段,你将在 all 构造函数中拥有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 构造函数调用。当然,不能创建任何对象,如果您尝试创建Foo 的实例,您将始终以StackOverflowException 结束。
我有两个问题:
为什么编译器完全允许递归构造函数调用?
为什么我们观察编译器对在此类中初始化的字段的这种行为?
一些注意事项:ReSharper 用Possible cyclic constructor calls 警告您。此外,在 Java 中,此类构造函数调用不会进行事件编译,因此 Java 编译器在这种情况下更具限制性(Jon 在网络研讨会上提到了此信息)。
这让这些问题变得更有趣,因为就 Java 社区而言,C# 编译器至少更现代。
【问题讨论】:
-
他**怎么会错过这个视频???
-
很好的问题。
-
不错的字段初始值设定项:
int a = null; int b = AppDomain.CurrentDomain; int c = "string to int"; int d = NonExistingMethod(); int e = Invalid<Method>Name<<Indeeed();应该做一个小测验:“这些字段声明在什么情况下可以?” (有一个关于未使用字段的警告,但您可以通过读取其中一个实例构造函数(或其他地方)的主体内的每个字段来消除该警告。) -
我相信这是允许的due to the same reason。
-
字段初始化被放入所有调用基本构造函数的构造函数中。因此,如果没有调用基本构造函数的构造函数,则字段初始化不会放在任何地方。至少那部分对我来说很有意义。并不是编译器不知道把它放在哪里,而是因为编译器注意到它没有必须把它放在任何地方。
标签: c#