2012-06-01 2 views
0

Я, безусловно, могу ответить на этот вопрос сам, написав фиктивный тест, но хочу знать, что люди думают по этому вопросу. Вот он:Перегрузка C# с переопределением

Какой метод будет вызывать, когда у нас есть одновременно перегрузка и переопределение? Я рассматриваю только перегрузку типа, а не перегрузку arity и когда тип перегрузки связан.

Позвольте мне бросить вам пример:

class AA {} 
class BB : AA {} 

class A { 
    public virtual void methodA(AA anAA) { Console.Write("A:methodA(AA) called"); } 
    public virtual void methodA(BB aBB) { Console.Write("A:methodA(BB) called"); } 
} 

class B : A { 
    public override void methodA(AA anAA) { Console.Write("B:methodA(AA) called"); } 
} 

new B().methodA(new BB());  // Case 1 
new B().methodA(new AA());  // Case 2 
new B().methodA((AA)new BB()); // Case 3 

Можете ли вы сказать, что произойдет в случае 1, 2 и 3?

Я лично считаю, что перегрузка является злом и что нет постоянного мышления, которое могло бы привести к предсказуемому ответу. И это полностью основано на соглашении, реализованном в компиляторе + vm.

EDIT: Если у вас есть какие-то сомнения о том, почему перегрузка зло вы можете прочитать сообщение в блоге от Gilad Brach

Благодарности

ответ

3

Перегруженные методы исключены из набора метода, когда компилятор определяет, какой метод для вызова. См. Алгоритм member lookup. Так что, когда вы звоните methodA по типу B, набор элементов с именем methodA от типа B и это базовый тип будет построен:

override B.methodA(AA) 
virtual A.methodA(AA) 
virtual A.methodA(BB) 

Затем члены с ovveride модификатором удаляется из набора:

virtual A.methodA(AA) 
virtual A.methodA(BB) 

Эта группа методов является результатом поиска. После этого применяется overload resolution, чтобы определить, какой элемент вызывать.

  1. A.methodA(BB) вызывается, потому что его аргумент соответствует параметру.
  2. A.methodA(AA) будет выбран, но это виртуальный метод, поэтому на самом деле назвать идет к B.method(AA)
  3. То же, что вариант 2
+0

вы имеете в виду JIT компилятор, не так ли? –

+0

Нет, я не имею в виду JIT-компилятор. Если у вас нет соответствующего метода для вызова, у вас будет ошибка времени компиляции. –

+0

Ваша первая ссылка сломана – mathk

6

Нет, это вполне предсказуемо. Сначала определяется сигнатура метода, то есть сначала определяется перегрузка . Затем вызывается самый переопределенный метод. Таким образом, выходной сигнал будет:

  • А: Methoda (ВВ) называется
  • Б: Methoda (АА) называется
  • Б: Methoda (АА) называется

Метод принимает экземпляр АА будет вызываться во вторых двух случаях, потому что это тип ссылки, которая передается, и это версия B, которая вызывается.Обратите внимание, что даже это приведет к тому же результату:

A instance = new B(); 
instance.methodA((AA)new BB()); // Case 3 
+0

Я не имел в виду, что код непредсказуем. Позвольте мне пояснить это. В этом примере компилятор должен добавить еще 2 аксиомы на языке. Перед переопределением применяется первая перегрузка. Возможно, это было в другом раунде, и это было бы вполне приемлемым. Второй динамический тип аксиом используется для поиска метода переопределения. И я могу добавить третью аксиому. Статический тип используется для поиска перегрузки. Все аксиомы, которые у меня есть, не предсказуемы. Любой другой язык мог бы выбрать совершенно другой набор аксиом. – mathk

+0

Компилятор разрешает подпись метода на любом строго типизированном языке. Это должно быть на первом месте. Переопределение обрабатывается кодом opv Callvirt IL, который запускает любую версию этого метода в рассматриваемом объекте в то время. Поскольку компилятор не знает, каким будет этот объект, это нужно будет обработать позже. Я не согласен. Порядок должен быть таким, как круг в C#, Java и т. Д. –

+0

Я предпочитаю использовать термин «время компиляции» или «время выполнения», поскольку они более точны, чем «более позднее время». Но предполагая, что «позднее время» - это время выполнения, то, что вы описываете, - это классический поиск методов, который вы можете найти на любом языке OO (CLOS, Simula, Smalltalk были теми, кто изобрел эту концепцию). Но я говорю, что переопределение могло быть применено до того, чтобы вы получили Callvirt IL во всех трех случаях. Только «новый метод A() .A (новый BB())' будет вызывать метод «A: methodA (BB)». Существует не семантическая причина, почему один путь, а не другой путь. – mathk

0

Я думаю, что результат будет таким

случай 1: Console.Write ("A: Methoda (BB) называется");

кейс 2: Console.Write ("B: methodA (AA), называемый");

case 3: Console.Write ("B: методA (AA), называемый");

в случае 3 будет выглядеть тот тип, который он прошел, и это B

Смежные вопросы