为什么不能在C#中动态调度基本访问表达式?

我认为这个问题最好通过一个例子来理解,所以我们在这里:

public class Base { // this method works fine public void MethodA(dynamic input) { // handle input } } public class Derived: Base { // Derived was named Super in my original post // This is also fine public void MethodB(dynamic input) { MethodA(input); } // This method does not compile and the compiler says: // The call to method 'MethodA' needs to be dynamically dispatched, // but cannot be because it is part of a base access expression. // Consider casting the dynamic arguments or eliminating the base access. public void MethodC(dynamic input) { base.MethodA(input); } } 

编译器明确指出方法C是无效的,因为它使用基本访问来调用方法A.但为什么呢?

如何在使用动态参数覆盖方法时调用基本方法?

例如,如果我想做什么:

  public class Base { // this method works fine public virtual void MethodA(dynamic input) { Console.WriteLine(input.say); } } public class Derived: Base { // Derived was named Super in my original post // this does not compile public override void MethodA(dynamic input) { //apply some filter on input base.MethodA(input); } } 

是的,这在设计上是行不通的。 base.MethodA()调用对虚拟方法进行非虚拟调用。 编译器在非动态情况下为此发出IL几乎没有问题,因为它知道需要调用哪种特定方法。

动态调度并非如此。 DLR的工作是确定需要调用哪种特定方法。 它必须使用的是Derived类的MethodTable。 该表不包含基类’MethodA方法的地址。 它被覆盖覆盖了。 它所能做的就是调用Derived.MethodA()方法。 这违反了基本关键字合同。

在您的示例中, Derived没有名为MethodA的方法,因此调用base.MethodA()与调用this.MethodA()相同,因此您可以直接调用该方法并完成它。 但我假设你也有一个不同的this.MethodA() ,你希望能够调用base.MethodA() 。 在这种情况下,只需听取编译器的建议并将参数转换为object (请记住, dynamic实际上只是编译器以特殊方式处理的object ):

 base.MethodA((object)input); 

问题不是动态参数,而是调用使用DLR进行调度(以下示例说明)。 也许DLR不支持这种呼叫。

 public class Base { public virtual void Method(int input) { } } public class Super : Base { public override void Method(int input) { dynamic x = input; base.Method(x); // invalid } } 

你可以用这个:

 ((Base)this).MethodA(input); 

规格说:

在绑定时,base.I和base [E]forms的基本访问表达式的计算方式与它们的编写完全相同((B)this).I和((B)this)[E],其中B是构造发生的类或结构的基类

所以,为什么你的例子给出了错误,这个结构编译得很好,这是个好问题。