为啥这两种方法没有歧义?
Posted
技术标签:
【中文标题】为啥这两种方法没有歧义?【英文标题】:Why are these two methods not ambiguous?为什么这两种方法没有歧义? 【发布时间】:2015-10-06 02:46:51 【问题描述】:这是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
中的方法。
编译器如何知道运行哪个方法?
【问题讨论】:
@M.kazemAkhgary 两种方法都返回相同的接口 第一个是internal
。您是从不同的程序集中致电 Ok()
吗?
写OkResult result = Ok();
会发生什么?
可选参数从调用者那里得到注入的默认值,所以它们是两个不同参数的方法
【参考方案1】:
Jon Skeet 回答了一个类似的问题(没有继承的复杂性)here:
当编译器有两个其他相等的选项可供选择时,它将使用不需要使用任何未提供的可选参数的重载,而不是使用的重载...
但是,在您的情况下,选择了 RestController
中的方法,因为它是更多派生类。 Jon 在他的书C# in Depth 中很好地解决了这个主题——看看那个页面的继承部分,它基本上表明编译器会更喜欢实际实例类的方法,而不是较少派生类的方法。
【讨论】:
@JamieHumphries - 对 base.Ok() 的调用将从 RestController 调用 ApiController.Ok(),但不会从 PersistenceRestController 调用。我相信 (this as ApiController).Ok() 将不可用,因为您正在转换为 ApiController,Ok() 方法在其上受到保护,而不是内部或公共。【参考方案2】:编辑:
我将我的原始答案留给后人,因为我认为它可以让您将事物可视化,但不要混淆!编译器实际上并不将可选参数视为重写方法的语法糖。它将其视为具有可选参数的单个方法。 Dusty 的回答中提到“选择来自 RestController 的方法是因为它是更多派生类”,这是正确的。
ORIGINAL(带有可见的修改以确保正确性):
因为它们不是模棱两可的。为了模棱两可,方法需要具有相同的签名。 string message
参数的默认值为 null 的事实实际上 create 表现得好像它创建了两个可调用的覆盖,其中一个隐藏了原始方法,其中一个明显可以用字符串调用.
您正在有效地做创建与您要这样做相同的行为:
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.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();
当您在 Foo3
类中调用 Bar()
时,运行时将在 Foo3
类中的方法之后查找。如果找到,就执行它,否则转到***类:Foo2
并关注Bar
方法。有没有?是的!所以执行它!这就是为什么当您调用Ok
时,您的RestController
s 版本会被执行。
而且,Foo2.Bar(string message = null)
不会与Foo.Bar()
冲突,因为它们并不像@DimitarTsonev 所说的那样模棱两可。因此,您的代码可以正常工作。
还有,从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()
【讨论】:
【参考方案4】:public class Foo
public void Bar()
public void Bar(string message = null)
这是两种不同的方法,因为第二种方法有可选参数。
但是,请注意,不带参数调用的第二个方法实际上会执行第一个方法,这可能会产生一些意想不到的行为。
【讨论】:
这与所问的情况不同,因为没有可选参数的方法应该位于基类上(调用Bar()
时会改变行为)。以上是关于为啥这两种方法没有歧义?的主要内容,如果未能解决你的问题,请参考以下文章
在 React Native 中,这两种方法中哪种 Async Await 方法更好,为啥?