Предположим, что мы имеем:
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 (Byte)
, как это:
((FooBase)writer).Write((byte)6);
, как правило, C # предпочтит использовать перегрузку, определенную на типе напрямую и бросить свой аргумент, а не использовать Перегрузка, определенная на родительском типе.
Это своего рода плохой стиль, чтобы использовать метод, следующий метод базового класса.
Это довольно часто задаваемый вопрос. Анализ Джона, конечно же, верен. Для дальнейшего прочтения, вот моя статья на эту тему:
http://blogs.msdn.com/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx
Да, это будет. По сути, это моя головоломка №1 . На самом деле это не вывод типа в том смысле, в котором он обычно используется, - это разрешение перегрузки. Вот где нужно искать в спец.
Теперь типом времени компиляции Writer
является Foo
.
Когда вы звоните писателю.Напишите
, компилятор начнет с типа Foo
и будет двигаться вверх по иерархии типов, пока не найдет метод, изначально объявленный в этом типе, который он может законно вызывать с указанными вами аргументами. Как только он найден, он не продвигается дальше по иерархии.
Теперь число 5 может быть преобразовано в десятичное
(равно как и 5 после того, как оно было специально преобразовано в байт
), поэтому 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
, тогда он будет вызван, потому что тогда это будет считаться объявлением нового метода.
Я думаю, что этот аспект противоречит здравому смыслу - и он не нужен для управления версиями, как будто вы переопределяете метод в дочернем классе, вы четко знаете, что он находится в базовом классе.
Мораль истории : постарайтесь не делать этого. В конечном итоге вы запутаете людей. Если вы унаследованы от класса, не добавляйте новые методы с тем же именем, но с другой подписью, если это возможно.