Подтвердить что ты не робот

Почему не может быть динамически отправлено выражение базового доступа в С#?

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

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

    }
4b9b3361

Ответ 1

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

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

Ответ 2

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

base.MethodA((object)input);

Ответ 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 и база [E] оценивается точно так, как если бы они были написаны ((B) это).I и ((B) это) [E], где B - основание класс класса или структуры, в котором конструкция имеет место

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