使用多个重载和默认参数调用特定的基方法

Calling specific base method with multiple overloads and default parameters

我正在构建一个游戏的源代码,并试图调用一个具有重载方法和默认参数的基本虚函数。我无法更改派生自的 classes,需要在我自己的 class 虚方法定义中调用该函数。我会尝试用代码更详细地解释。

首先我们有一个基础 class A,它定义了一个名为 Foo 的虚函数,它接受一个参数。

class A
{
   public virtual string Foo(int a)
   {
      return "Class A Function 1 par";
   }
}

然后是一个 class B,它覆盖了 Foo 并为 Foo 定义了一个新的重载虚函数,带有两个新的默认参数。

class B : A
{
   public virtual string Foo(int a, int b = 0, int c = 0)
   {
      return "Class B Function 3 par";
   }

   public override string Foo(int a)
   {
      return "Class B Function 1 par";
   }
}

然后 class 我需要派生自 C。它只是覆盖了一个参数 Foo。

class C : B
{
   public override string Foo(int a)
   {
      return "Class C Function 1 par";
   }
}

最后是我的 class D,它也覆盖了一个参数 Foo,但还需要能够调用 Foo 的基本方法。

class D : C
{
   public override string Foo(int a)
   {
      return base.Foo(0);
   }
}

这导致调用 B 中定义的三个参数 Foo(returning 为 "Class B Function 3 par"),但我想调用 C 中定义的 Foo(return "Class C Function 1 par").我认为这种使用默认参数重载虚函数会导致编译器出现不明确的错误,但它编译得很好。

有没有办法解决这个问题,如果没有,为什么它允许 classes 的结构阻止我访问基本方法?

这是可选参数与语言其余部分之间的不幸交互,也是您基本上永远不应该重载使用可选参数的方法(或向不使用可选参数的方法添加重载)的一个很好的理由。 B的作者做了一件非常糟糕的事情!

调用没有歧义,因为 base 的方法查找规则没有改变,只有在重载解析后最终调用方法的规则 -- 实际上,重载是确定的就好像呼叫已读取 ((C) this).Foo(0)。对于那个调用,B.Foo(int, int, int) 被认为是唯一的候选者,因为它是沿着继承链向上走时最近的非 override 方法——它在我们开始考虑 A.Foo(int) 之前就被选中了。如果 A 引入了该方法,就不会有问题,因为在这种情况下,单参数重载会被认为是更好的方法。

如果可选参数从一开始就是 C# 的一部分,而不是相对较晚才添加到聚会中(有一些古怪的实现,其中值在调用站点扩展),这可能已经被考虑过并且以某种方式减轻。就目前而言,解决此问题的最明显方法是实际更改 base 查找规则,因此它更喜欢匹配与它所在方法的签名完全匹配的方法,但这只会使已经复杂的复杂化重载决策的规则更多,而且它肯定会破坏现有代码,因此发生这种情况的可能性很小。

如果您无法更改 ABC,仍然可以通过利用(如果就是这个词)另一个相当晦涩的 C# 功能甚至更古老:方法组!

class D : C
{
   public override string Foo(int a) 
   {
      Func<int, string> foo = base.Foo;
      return foo(a);
   }
}    

这明确地调用了 C.Foo(int),因为方法组的委托转换不考虑可选参数,因此 B.Foo(int, int, int) 不是有效的候选者,迫使我们进一步向上移动并找到 A.Foo(int).