为什么这两种方法不含糊?

时间:2015-07-16 15:02:07

标签: c# asp.net .net methods overloading

这是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 个答案:

答案 0 :(得分:11)

Jon Skeet回答了一个类似的问题(没有继承并发症)here

  

当编译器有两个相同的选项可供选择时,它将使用一个重载,它不需要使用任何不匹配的可选参数,优先于那个...

但是,在您的情况下,正在选择RestController中的方法,因为它是更多派生类。 Jon在他的书C# in Depth中详细讨论了这个主题 - 看一下该页面的继承部分,它基本上表明编译器在实现较少派生的方法之前更喜欢实际实例类的方法类。

答案 1 :(得分:4)

修改

我将原始答案留给后代,因为我认为它可以让你想象事物,但不要混淆!编译器实际上并不将可选参数视为重写方法的语法糖。它将它视为具有可选参数的单个方法。 Dusty的回答,提到“选择RestController中的方法是因为它是派生类更多”,这是正确的。

ORIGINAL(可见的正确编辑):

因为它们不含糊不清。为了模糊,方法需要具有相同的签名。事件string message参数的默认值为null 创建了 BEHAVES,好像它创建了两个可调用的覆盖,其中一个隐藏了原始方法,其中一个可以明显调用一个字符串。

您有效地创建了与执行此操作相同的行为:

public class RestController : ApiController
{
    protected new OkResult Ok()
    {
        return Ok(null);
    }

    protected OkResult Ok(string message)
    {
        // Do your thing...
    }
}

你会发现没有办法直接从PersistenceRestController调用ApiController.Ok()。

如果你想从RestController调用ApiController.Ok(),你将不得不使用base keywoard:base.Ok();

答案 2 :(得分: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'版本会被执行。

但是,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()
    }
}

答案 3 :(得分:1)

public class Foo
{
    public void Bar()
    {
    }

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

这是两种不同的方法,因为第二种方法有可选参数。

但请注意,没有参数调用的第二个方法实际上会执行第一个方法,这可能会产生一些意外行为。