2010-08-05 1 views
22

Я думаю, этот вопрос лучше всего можно понять на примере, так что здесь мы идем:Почему не может быть динамически отправлено выражение с базовым доступом в 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 является недействительным в связи с тем, что он использует базовый доступ для вызова метода А. Но почему в том, что?

И как можно вызвать базовый метод при переопределении метода с динамическими параметрами?

E.g. что, если я хотел бы сделать:

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

    } 

ответ

19

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

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

+1

Ahh, наконец, кто-то, кто может ответить * почему *. Что вы подразумеваете под «Суперклассом»? Я знаю этот термин как синоним «базового класса». –

+0

@Allon: это фрагмент OP. –

+0

@Hans: О, pfff, глупо меня. –

8

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

base.MethodA((object)input); 
+1

Я уверен, что вызов его через базу - это требование стать базовым выражением (это мой вопрос). ;) Использование объекта в качестве аргумента является наилучшим обходным решением до сих пор, но все же я ищу ответ на вопрос, почему мне не разрешено передавать динамический базовый метод. Какова основная проблема? –

-2

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

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

Спецификация сказала:

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

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

+0

Я не думаю, что это всегда будет работать. Например, если вы хотите вызвать метод базового класса, и этот метод будет помечен как виртуальный, ваш приведение приведет к вызову переопределенного метода. –

+0

Да, это не сработает для виртуальных методов ... –

-1

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

Очевидно, что это не поддерживается, но почему? Вот в чем вопрос. –