Почему базовое выражение доступа не может быть динамически отправлено в C #? - PullRequest
24 голосов
/ 05 августа 2010

Я думаю, что этот вопрос лучше всего понять на примере, поэтому мы идем:

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

    }

Ответы [ 4 ]

20 голосов
/ 05 августа 2010

Да, это не может сработать.Вызов base.MethodA () выполняет не виртуальный вызов виртуального метода.У компилятора нет особых проблем с передачей IL для этого в нединамическом случае, поскольку он знает, какой конкретный метод необходимо вызвать.

Это не относится к динамической диспетчеризации.Задача DLR - выяснить, какой конкретный метод необходимо вызвать.То, с чем он должен работать, является MethodTable класса Derived.Эта таблица не содержит адрес метода MethodA базового класса.Это было перезаписано переопределением.Все, что он может сделать, это вызвать метод Derived.MethodA ().Что нарушает базовый контракт ключевых слов.

9 голосов
/ 05 августа 2010

В вашем примере Derived не имеет метода с именем MethodA, поэтому вызов base.MethodA() аналогичен вызову this.MethodA(), поэтому вы можете просто вызвать метод напрямую и покончить с ним.Но я предполагаю, что у вас также есть другой this.MethodA(), и вы хотите иметь возможность звонить base.MethodA().В этом случае просто послушайте совет компилятора и приведите аргумент к object (помните, что dynamic на самом деле просто object, что компилятор обрабатывает особым образом):

base.MethodA((object)input);
0 голосов
/ 05 августа 2010

Проблема не в динамическом аргументе, а в том, что вызов использует 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
    }
}
0 голосов
/ 05 августа 2010

Вы можете использовать это:

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

В спецификации сказано:

Во время привязки, базовый доступ выражения формы base.I и База [E] оцениваются точно так же, как если бы они были написаны ((B) это). Я и ((B) это) [E], где B является основанием класс класса или структуры, в которой конструкция происходит

Итак, почему ваш пример дает ошибку, а эта конструкция хорошо компилируется, это хороший вопрос.

...