2010-01-29 2 views
9

Пусть мы имеем:Метод заказа разрешение

public class FooBase 
{ 
    public void Write(byte value) 
    { 
     //something 
    } 

    public void Write(int value) 
    { 
     //something 
    } 
} 

public class Foo : FooBase 
{ 
    public void Write(decimal value) 
    { 
     //something 
    } 
} 

, чем это:

 var writer = new Foo(); 

     writer.Write(5);   //calls Write(decimal) !! 
     writer.Write((byte)6); //calls Write(decimal) !! 

будет вызывать запись (десятичное) перегрузки. Зачем? И как я могу назвать Write (int) или Write (byte)?

+0

Предлагайте переименовании вопрос к чему-то дело с «методом разрешения порядка». – dss539

ответ

7

Вы могли бы назвать Write(byte) так:

((FooBase)writer).Write((byte)6); 

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

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

+0

Точно. Лучшее решение - просто использовать лучшие имена, чтобы избежать двусмысленности. – captncraig

+0

Я думаю, что это неправильно: когда вы создаете экземпляр экземпляра для базового класса, он все равно является одним и тем же экземпляром производного класса, поэтому его перегруженный метод будет выполнен. Я что-то упускаю? – Isantipov

+0

Это экземпляр, поскольку до сих пор существует сам объект в памяти. Однако при кастинге вы эффективно говорите компилятору, чтобы рассматривать этот экземпляр как другой тип. IDE больше не будет распознавать перегрузку. – EnderWiggin

16

Да, это будет сделано. Это действительно мой brainteaser #1. Это не типичный вывод в том смысле, что он обычно используется - это разрешение перегрузки. Вот где вам нужно посмотреть в спецификации.

Теперь тип времени компиляции Writer - Foo.

Когда вы вызываете writer.Write, компилятор начнет с типа Foo и проделает путь вверх по типу hiearchy, пока не найдет метод, первоначально объявленный в этом типе, который он может законно вызывать с аргументами, которые вы указали. Как только он будет найден, он не продвинется дальше по иерархии.

Теперь, 5 конвертируется в decimal (и так 5 после того, как он был специально брошен в byte) - так Foo.Write(decimal) является применяется функция член для вызова метода - и это то, что вызывается. Он даже не рассматривает перегрузки FooBase.Write, потому что он уже найден.

До сих пор это разумно - идея заключается в том, что добавление метода к базовому типу не должно изменять разрешение перегрузки для существующего кода, где дочерний тип не знает об этом. Это немного падает, когда речь идет о переопределении. Давайте немного изменить свой код - я собираюсь удалить версию byte, сделать Write(int) виртуальные и переопределить его в Foo:

public class FooBase 
{ 
    public virtual void Write(int value) 
    { 
     //something 
    } 
} 

public class Foo : FooBase 
{ 
    public override void Write(int value) 
    { 
     //something 
    } 

    public void Write(decimal value) 
    { 
     //something 
    } 
} 

Что теперь будет new Foo().Write(5) делать? Это будет еще позвонить Foo.Write(decimal) - потому что Foo.Write(int) не было объявлено в Foo, только переопределено там. Если вы измените override на new, тогда он будет вызываться, потому что тогда это считается объявлением нового метода.

Я думаю, что этот аспект противоречит интуиции - и он не нужен для управления версиями, как если бы вы переопределяли метод в дочернем классе, вы четко знаете, что это в базовом классе.

Мораль истории: постарайтесь не делать этого. Вы в конечном итоге запутаете людей. Если вы выходите из класса, не добавляйте новые методы с тем же именем, кроме другой, если вы можете помочь.

+0

Я думал о вашем головоломке, когда я тоже читал этот вопрос ... –

+0

+1 для вывода. Не делай этого. – jeroenh

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