Почему эти два метода не являются двусмысленными?
Это подпись для метода Ok()
в ApiController
:
protected internal virtual OkResult Ok();
И это мой метод из моего класса RestController
(который простирается от ApiController
):
// Note that I'm not overriding base method
protected IHttpActionResult Ok(string message = null);
Так как OkResult
реализует IHttpActionResult
, оба этих метода можно вызвать так:
IHttpActionResult result = Ok();
Фактически, это то, что я делаю в своем приложении.
Мой класс PersistenceRestController
(который простирается от RestController
), имеет следующие строки кода:
protected override async Task<IHttpActionResult> Delete(Key id)
{
bool deleted = //... Attempts to delete entity
if(deleted) return Ok();
else return NotFound();
}
Это компилируется отлично, и не возникает предупреждения о неоднозначности метода. Почему это?
PersistenceRestController
также унаследовал защищенные методы от ApiController
, поэтому он должен иметь обе версии Ok()
(и он делает).
При выполнении, выполненный метод является тем из моих RestController
.
Как компилятор знает, какой метод запускать?
Ответы
Ответ 1
Джон Скит ответил на аналогичный вопрос (без усложнения наследования) здесь:
Когда у компилятора есть два варианта, равных в противном случае, он будет использовать перегрузку, которая не нуждается в каких-либо необязательных необязательных параметрах, чем тот, который делает...
В вашем случае, однако, выбирается метод из RestController
, потому что это более производный класс. Джон неплохо занимается подробным рассмотрением этой темы в своей книге С# в глубине - посмотрите раздел наследования этой страницы, который по существу заявляет, что компилятор предпочтет метод для фактического класса экземпляра перед методами на менее производных классах.
Ответ 2
EDIT:
Я оставляю свой первоначальный ответ для потомков, потому что я думаю, что он позволяет вам визуализировать вещи, но НЕ БУДЕТ КОНФЛИКТОВ! Компилятор фактически не обрабатывает необязательный параметр как синтаксический сахар для переопределенного метода. Он рассматривает его как единый метод с необязательным параметром. Пыльный ответ, отметив, что "метод из RestController выбирается, потому что он является более производным классом". Правильно.
ОРИГИНАЛ (с видимыми изменениями для правильности):
Потому что они НЕ двусмысленны. Чтобы быть неоднозначным, методы должны иметь одну и ту же подпись. Тот факт, что параметр string message
имеет значение по умолчанию null эффективно создает BEHAVES, как если бы он создавал два вызываемых переопределения, один из которых HIDES оригинальный метод, и один из которых явно вызываемый со строкой.
Эффективно выполняете , создавая такое же поведение, как если бы вы это делали:
public class RestController : ApiController
{
protected new OkResult Ok()
{
return Ok(null);
}
protected OkResult Ok(string message)
{
// Do your thing...
}
}
Вы обнаружите, что нет прямого вызова ApiController.Ok() из PersistenceRestController.
Если вы хотите вызвать ApiController.Ok() из RestController, вам нужно будет использовать базовый keywoard: base.Ok();
Ответ 3
В то время как @DimitarTsonev и @Dusty говорят истинные вещи, но ваш ответ - это что-то среднее между их ответами. Здесь у вас есть ситуация наследования. См. Следующие классы:
public class Foo {
public void Bar() {
}
}
public class Foo2 : Foo{
public void Bar(string message = null) {
}
}
public class Foo3 : Foo2{
public void Test(){
Bar();
}
}
Когда вы вызываете Bar()
в свой класс Foo3
, среда выполнения будет искать после метода внутри класса Foo3
. Если он найден, выполните его, в противном случае перейдите в верхний класс: Foo2
и обратите внимание на метод Bar
. Есть ли? да! поэтому выполните его! поэтому, когда вы вызываете Ok
, ваша версия RestController
s будет выполнена.
Но также Foo2.Bar(string message = null)
не будет конфликтовать с Foo.Bar()
, потому что они НЕ двусмысленны, как сказал @DimitarTsonev. Таким образом, ваш код будет работать нормально.
И, как насчет вызова Foo.Bar()
из Foo3
? Вы должны использовать кастинг здесь:
public class Foo3 : Foo2 {
public void Test() {
Bar(); // this will execute Foo2.Bar()
}
public void Test2() {
((Foo)this).Bar(); // this one will execute Foo.Bar()
}
}
Ответ 4
public class Foo
{
public void Bar()
{
}
public void Bar(string message = null)
{
}
}
Это два разных метода, потому что второй имеет необязательный аргумент.
Однако обратите внимание, что второй метод, вызываемый без аргументов, фактически выполнит первый, что может привести к неожиданному поведению.