Reputation: 26281
This is the signature for the Ok()
method in ApiController
:
protected internal virtual OkResult Ok();
And this is my method from my RestController
class (which extends from ApiController
):
// Note that I'm not overriding base method
protected IHttpActionResult Ok(string message = null);
Since OkResult
implements IHttpActionResult
, both of these methods can be called like this:
IHttpActionResult result = Ok();
In fact, that's what I'm doing in my application.
My class PersistenceRestController
(which extends from RestController
), has these lines of code:
protected override async Task<IHttpActionResult> Delete(Key id)
{
bool deleted = //... Attempts to delete entity
if(deleted) return Ok();
else return NotFound();
}
This compiles fine, and no warning is raised about method ambiguity. Why is that?
PersistenceRestController
has also inherited the protected methods from ApiController
so it should have both versions of Ok()
(and it does).
At execution, the method executed is the one from my RestController
.
How does the compiler know which method to run?
Upvotes: 15
Views: 1039
Reputation: 9237
EDIT:
I am leaving my original answer for posterity because I think it lets you visualize things, but DO NOT BE CONFUSED! The compiler does not actually treat the optional parameter as syntactic sugar for an overridden method. It treats it as a single method with an optional parameter. Dusty's answer, mentioning that "the method from the RestController is being chosen because it's the more derived class," is correct.
ORIGINAL (With visible edits for correctness):
Because they are NOT ambiguous. In order to be ambiguous the methods need to have the same signature. The fact that the string message
parameter has a default value of null effectively creates BEHAVES as though it creates two callable overrides, one of which HIDES the original method, and one of which is distinctly callable with a string.
You are effectively doing creating the same behavior as if you were to do this:
public class RestController : ApiController
{
protected new OkResult Ok()
{
return Ok(null);
}
protected OkResult Ok(string message)
{
// Do your thing...
}
}
You will find there is no way to directly call ApiController.Ok() from PersistenceRestController.
If you want to call ApiController.Ok() from RestController, you'll have to use the base keywoard: base.Ok();
Upvotes: 3
Reputation: 27585
While @DimitarTsonev and @Dusty are telling true stuffs, but your answer is something between their answers. Here, you have inheritance situation. See these classes:
public class Foo {
public void Bar() {
}
}
public class Foo2 : Foo{
public void Bar(string message = null) {
}
}
public class Foo3 : Foo2{
public void Test(){
Bar();
}
}
When you call Bar()
in your Foo3
class, the runtime will lookup after the method inside the Foo3
class. If found it, execute it, otherwise go to the top class: Foo2
and look after Bar
method. Is there any? yes! so execute it! that's why when you call Ok
, your RestController
s' version get executed.
But also, the Foo2.Bar(string message = null)
will not conflict with Foo.Bar()
because they are NOT ambiguous as @DimitarTsonev said. So, your code will work just fine.
AND, what about calling Foo.Bar()
from Foo3
? You have to use casting here:
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()
}
}
Upvotes: 2
Reputation: 3971
Jon Skeet answered a similar question (without the inheritance complication) here:
When the compiler has two otherwise-equal options to choose from, it will use an overload which doesn't need use any unsupplied optional parameters in preference to one that does...
In your case, however, the method from the RestController
is being chosen because it's the more derived class. Jon does a good job of addressing the topic in detail in his book C# in Depth -- look at the inheritance section of that page, which essentially states that the compiler will prefer a method on the actual instance class before methods on less derived classes.
Upvotes: 10
Reputation: 3882
public class Foo
{
public void Bar()
{
}
public void Bar(string message = null)
{
}
}
Those are two different methods because the second has the optional argument.
However, please note that the second method called with no arguments will actually execute the first one, which may produce some unexpected behaviour.
Upvotes: 1