Почему эти два метода не являются неоднозначными?

Это подпись для 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,

Как компилятор узнает, какой метод запустить?

4 ответа

Решение

Джон Скит ответил на аналогичный вопрос (без усложнения наследования) здесь:

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

В вашем случае, однако, метод из RestController выбирается потому, что это более производный класс. Джон проделывает хорошую работу по подробному рассмотрению этой темы в своей книге C# in Depth - посмотрите на раздел наследования на этой странице, в котором, по сути, говорится, что компилятор предпочтет метод реального класса экземпляра, а не методы менее производных классов.

РЕДАКТИРОВАТЬ:

Я оставляю свой первоначальный ответ для потомков, потому что я думаю, что он позволяет вам визуализировать вещи, но НЕ БУДЬТЕ ЗАМЯТЫ! Компилятор фактически не обрабатывает необязательный параметр как синтаксический сахар для переопределенного метода. Он обрабатывает это как один метод с необязательным параметром. Ответ Дасти с упоминанием о том, что "метод из RestController выбирается, потому что это более производный класс", является правильным.

ОРИГИНАЛ (с видимыми правками):

Потому что они НЕ двусмысленны. Чтобы быть неоднозначными, методы должны иметь одинаковую сигнатуру. Тот факт, что string message Параметр со значением по умолчанию, равным null, эффективно создает BEHAVES, как если бы он создавал две вызываемые переопределения, одна из которых скрывает оригинальный метод, а другая явно вызывается строкой.

Вы фактически делаете создание такого же поведения, как если бы вы делали это:

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, вам нужно будет использовать базовую клавиатуру: base.Ok();

Хотя @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, ваш RestControllerS 'версия исполняется.

Но также, 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()
    }
}
public class Foo
{
    public void Bar()
    {
    }

    public void Bar(string message = null)
    {
    }
}

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

Однако обратите внимание, что второй метод, вызываемый без аргументов, будет фактически выполнять первый, что может привести к неожиданному поведению.

Другие вопросы по тегам