.net 过载分辨率异常

bkhjykvo  于 2023-10-21  发布在  .NET
关注(0)|答案(4)|浏览(98)

不知道这是否是C# 4+特定的,但刚刚注意到这一点。
考虑以下类:

class Base
{
  protected void Foo(object bar, DayOfWeek day)
  {
  }
}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  void Bar(DayOfWeek day)
  {
    Foo(new { day }, day);
  }
}

Bar中对Foo的调用解析为Foo(object, object)
同时将其更改为:

class Base
{

}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  protected void Foo(object bar, DayOfWeek day)
  {
  }

  void Bar(DayOfWeek day)
  {
    Foo(new { day }, day);
  }
}

Bar中对Foo的调用解析为Foo(object, DayOfWeek)
我的理解是,它应该总是像第二个例子那样解决。
这是一个“错误”还是我缺乏理解(或无知)?

更新:

谢谢你的回答。正如我所发现的,可以使用base.来调用基类中的方法。然而,当在混合中添加另一个派生类时,问题又回来了。

class Base
{
  protected void Foo(object bar, DayOfWeek day)
  {
  }
}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  void Bar(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

class Derived : Program
{
  void Baz(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

base.调用在Program中工作,但在Derived中解析为Foo(object, object)
那么,如何从Derived调用Foo(object,DayOfWeek),而不必在Program中创建“冗余”方法?

siv3szwd

siv3szwd1#

我认为对于解析方法调用,它首先在它的类中查找,因为DayOfWeek可以作为object类型传递,它调用类自己的方法,而不是基类的方法。
在第二种情况下,方法调用解析为更特定的类型参数,因此Foo(object bar, DayOfWeek day)被调用。
来自MSDN -Method resolution.
如果派生类中的任何方法都适用,则基类中的方法不是候选方法(第7.5.5.1节)。

  • 给定适用的候选函数成员的集合,定位该集合中的最佳函数成员。
  • 如果集合只包含一个函数成员,则该函数成员是最佳函数成员。
  • 否则,最好的函数成员是相对于给定的参数列表优于所有其他函数成员的一个函数成员,前提是每个函数成员都使用第7.4.2.2节中的规则与所有其他函数成员进行比较。
  • 如果没有一个函数成员比所有其他函数成员都好,那么函数成员调用是不明确的,并且会发生编译时错误。
wqnecbli

wqnecbli2#

你需要看看埃里克·利珀特的博客-特别是这篇文章:https://learn.microsoft.com/en-us/archive/blogs/ericlippert/future-breaking-changes-part-three
不过,重载解析算法实际上是在当前类中搜索可以调用的重载,并且只在当前类中找不到重载时才在基类中搜索替代重载。
在第一种情况下,Foo(object, object)过载是适用的,因此不执行进一步的搜索。
在第二种情况下,Foo(object DayOfWeek)更好,因此使用它。
阅读Eric的文章了解详细信息。

gfttwv5a

gfttwv5a3#

我觉得这个规格很合理。派生类程序员不需要知道基类中 unrelative 方法的实现。否则,一个倒霉的家伙写了一个方法,它的兼容性比它的基类中的方法差(这个家伙不知道基类的细节),然后基类中的方法被调用。

zzoitvuj

zzoitvuj4#

正如其他人所指出的,这个问题与重载解决方案的工作方式有关。再补充一点:
如果Base.Foopublic,那么你可以在Derived中得到Base.Foo(假设Base.Foo没有被覆盖):

((Base)this).Foo(new { day }, day);

此外,您可以选择覆盖(如果您可以将Base.Foo更改为virtual)或在Program中显式隐藏Base.Foo,因此当您在Derived中调用base.Foo时,它仍然会调用Base.Foo

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  protected new void Foo(object bar, DayOfWeek baz)
  {
    base.Foo(bar, baz);
  }

  void Bar(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

作为旁注:* 通常 *,派生类提供的重载比它们的基类重载具有 * 更具体 * 的参数类型(例如,Object.Equals(object)String.Equals(string))。
即使有一个不太(或同样)具体的参数类型的情况下,他们要么 * 覆盖 * 基本方法(例如,Object.Equals(object)String.Equals(object)->重写Object.Equals(object)),或者它们只是给予不同的方法名。

相关问题