在重构一些代码时,我遇到了这个奇怪的编译错误:
构造函数调用需要动态分派,但不能,因为它是构造函数初始值设定项的一部分。考虑转换动态参数。
当尝试调用采用动态参数的基本方法/构造函数时,似乎会发生这种情况。例如:
class ClassA
{
public ClassA(dynamic test)
{
Console.WriteLine("ClassA");
}
}
class ClassB : ClassA
{
public ClassB(dynamic test)
: base(test)
{
Console.WriteLine("ClassB");
}
}
如果我将参数转换为
object
,它就会起作用,如下所示:
public ClassB(dynamic test)
: base((object)test)
所以,我有点困惑。为什么我必须放入这个令人讨厌的转换 - 为什么编译器不能弄清楚我的意思?
必须在编译时确定构造函数链 - 编译器必须选择一个重载,以便它可以创建有效的 IL。虽然通常可以将重载解析(例如方法调用)推迟到执行时间,但这对于链式构造函数调用不起作用。
编辑:在“普通”C# 代码中(基本上在 C# 4 之前),all 重载决策在编译时执行。但是,当成员调用涉及动态值时,该值将在执行时解析。例如考虑这个:
using System;
class Program
{
static void Foo(int x)
{
Console.WriteLine("int!");
}
static void Foo(string x)
{
Console.WriteLine("string!");
}
static void Main(string[] args)
{
dynamic d = 10;
Foo(d);
}
}
编译器不会在这里发出对
Foo
的直接调用 - 它不能,因为在调用 Foo(d)
中,它不知道它将解析为哪个重载。相反,它会发出执行某种“及时”小型编译的代码,以解决执行时 d
值的 actual类型的重载。
现在这不适用于构造函数链接,因为有效的 IL 必须包含对特定基类构造函数的调用。 (我不知道动态版本是否无法在IL中表达,或者是否可以,但结果将无法验证。)
您可能会争辩说,C# 编译器应该能够知道实际上只有一个可见的构造函数可以被调用,并且该构造函数将永远可用......但是一旦您开始沿着这条路走下去,您最终会使用一种“非常”难以指定的语言。 C# 设计者通常采取的立场是使用更简单的规则,但这些规则有时并不像您希望的那么强大。