【问题标题】:Why can't a base access expression be dynamically dispatched in C#?为什么不能在 C# 中动态分派基本访问表达式?
【发布时间】:2010-08-05 11:36:49
【问题描述】:

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

    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);
        }

    }

【问题讨论】:

    标签: c# dynamic c#-4.0


    【解决方案1】:

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

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

    【讨论】:

    • 啊,终于有人可以回答为什么了。 “超级班”是什么意思?我只知道这个词是“基类”的同义词。
    • @Allon:它在 OP 的 sn-p 中。
    • 在示例中命名子类 Super 对我来说是一个糟糕的举动。我应该把它命名为子类。我将在我的原始帖子中对此进行更改,以便将来对于遇到此问题的其他人来说不会那么混乱。 @Hans,谢谢你的回答!
    • @Ola:使用 Base + Derived,永远不要猜测。
    【解决方案2】:

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

    base.MethodA((object)input);
    

    【讨论】:

    • 我很确定通过 base 调用它是它成为基本表达式的必要条件(这是我的问题)。 ;) 使用对象作为参数是迄今为止最好的解决方法,但我仍在寻找一个答案,为什么我不允许将动态传递给基本方法。这里真正的根本问题是什么?
    【解决方案3】:

    问题不在于动态参数,而是调用使用 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
        }
    }
    

    【讨论】:

    • 显然不支持,但是为什么呢?这就是这里的问题。
    【解决方案4】:

    你可以用这个:

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

    规范说:

    在绑定时,基本访问 base.I 形式的表达式和 base[E] 的计算方式与 他们被写成((B)this)。我和 ((B)this)[E],其中 B 是基数 类或结构的类,其中 构造发生

    那么,为什么你的例子会出错,而这个结构编译得很好,这是个好问题。

    【讨论】:

    • 我认为这不会一直有效。例如,如果您想调用基类的方法并且该方法被标记为虚拟,您的强制转换将导致调用被覆盖的方法。
    猜你喜欢
    • 2014-04-12
    • 1970-01-01
    • 2017-10-08
    • 1970-01-01
    • 2022-07-23
    • 1970-01-01
    • 2013-03-05
    • 2010-12-19
    • 1970-01-01
    相关资源
    最近更新 更多