为什么这两种方法不含糊

本文关键字:不含糊 方法 两种 为什么 | 更新日期: 2023-09-27 18:03:07

这是ApiController:中Ok()方法的签名

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的方法。

编译器如何知道要运行哪种方法?

为什么这两种方法不含糊

Jon Skeet在这里回答了一个类似的问题(没有继承复杂性(:

当编译器有两个相等的选项可供选择时,它将使用一个重载,该重载不需要使用任何未应用的可选参数,而需要使用。。。

然而,在您的情况下,之所以选择RestController中的方法,是因为它是派生性更强的类。Jon在他的书《深度C#》中很好地详细阐述了这个主题——看看该页的继承部分,它本质上表明编译器更喜欢实际实例类上的方法,而不是派生较少的类的方法。

编辑:

我将把我最初的答案留给子孙后代,因为我认为它可以让你想象事物,但不要困惑!编译器实际上并没有将可选参数视为重写方法的语法糖。它将其视为具有可选参数的单个方法。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((;

虽然@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();
    }
}

当您在Foo3类中调用Bar()时,运行时将查找Foo3类中的方法。如果找到它,请执行它,否则转到顶级类:Foo2并查找Bar方法。有吗?对所以执行吧!这就是为什么当您调用Ok时,您的RestController的版本会被执行。

但同时,Foo2.Bar(string message = null)不会与Foo.Bar()冲突,因为它们并不像@DimitarTsonev所说的那样模棱两可。因此,您的代码将正常工作。

AND,从Foo3调用Foo.Bar()怎么样?你必须在这里使用铸造:

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)
    {
    }
}

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

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