2015-07-16 2 views
16

Это подпись для метода 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.

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

+0

Возможно, тип возврата? –

+0

@ M.kazemAkhgary Оба метода возвращают один и тот же интерфейс –

+0

Первый - 'internal'. Вы вызываете 'Ok()' из другой сборки? – Icemanind

ответ

11

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

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

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

+1

@JamieHumphries - вызов функции base.Ok() вызовет ApiController.Ok() из RestController, но не из PersistenceRestController. Я считаю (это как ApiController) .Ok() не будет доступен, поскольку вы отправляете ApiController, на котором защищен метод Ok(), а не внутренний или общедоступный. –

1
public class Foo 
{ 
    public void Bar() 
    { 
    } 

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

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

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

+0

. Это не та же ситуация, что и вопрос, потому что метод без дополнительных параметров должен быть в базовом классе (который меняет поведение при вызове 'Bar () '). –

4

EDIT:

Я покидаю свой оригинальный ответ для потомков, потому что я думаю, что это позволяет визуализировать вещи, но не смущайтесь! Компилятор фактически не обрабатывает необязательный параметр как синтаксический сахар для переопределенного метода. Он рассматривает его как единый метод с необязательным параметром. Ответ Дасти, отметив, что «метод из RestController выбирается, потому что это более производный класс», является правильным.

ORIGINAL (с видимыми редактированием для корректности):

потому что они не являются неоднозначными. Чтобы быть неоднозначным, методы должны иметь одну и ту же подпись. Тот факт, что параметр string message имеет значение по умолчанию null эффективно создает ПОВЕДЕНИЯ, как будто он создает два вызываемых переопределения, один из которых 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, вам придется использовать базовый ключ: 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() 
    } 
} 
+1

большое спасибо ... – Nildarar

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