Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are these two methods not ambiguous?

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?

like image 499
Matias Cicero Avatar asked Jul 16 '15 15:07

Matias Cicero


People also ask

What is ambiguous method?

This ambiguous method call error always comes with method overloading where compiler fails to find out which of the overloaded method should be used. Suppose we have a java program like below.

Is ambiguous error c++?

The declaration of a member with an ambiguous name in a derived class is not an error. The ambiguity is only flagged as an error if you use the ambiguous member name. For example, suppose that two classes named A and B both have a member named x , and a class named C inherits from both A and B .


2 Answers

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.

like image 114
Dusty Avatar answered Sep 19 '22 04:09

Dusty


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();

like image 43
bopapa_1979 Avatar answered Sep 20 '22 04:09

bopapa_1979