我正在构建游戏的源代码之上,并尝试使用重载方法和默认参数调用基本虚函数。我无法更改我从中派生的类,需要在我自己的虚拟方法类定义中调用该函数。我将尝试用代码更详细地解释。
首先,我们有一个基类A,它定义了一个名为Foo的虚函数,它接受一个参数。
class A
{
public virtual string Foo(int a)
{
return "Class A Function 1 par";
}
}
然后是一个覆盖Foo的B类,它为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";
}
}
然后我需要从C派生的类。它只是覆盖了一个参数Foo。
class C : B
{
public override string Foo(int a)
{
return "Class C Function 1 par";
}
}
最后我的D类也覆盖了一个参数Foo,但也需要能够调用Foo的基本方法。
class D : C
{
public override string Foo(int a)
{
return base.Foo(0);
}
}
这导致调用B中定义的三个参数Foo(返回“Class B Function 3 par”)但我想调用C中定义的Foo(将返回“Class C Function 1 par”)。我认为这种带有默认参数的虚函数重载会导致编译器错误,但编译错误。
有没有办法解决这个问题,如果不是为什么它允许类的结构阻止我访问基本方法?
这是可选参数与语言其余部分之间的不幸交互,这是一个很好的理由,为什么你基本上不应该重载使用可选参数的方法(或者将重载添加到不使用的方法)。 B
的作者做了一些非常糟糕的事情!
调用不是模糊的,因为方法查找的规则不会改变base
,只有在重载解析后最终调用哪个方法的规则 - 实际上,重载被确定为调用已经读取了((C) this).Foo(0)
。对于那个调用,B.Foo(int, int, int)
被认为是唯一的候选者,因为它是走近继承链时最接近的非override
方法 - 它在我们考虑A.Foo(int)
之前被选中。如果A
介绍了这种方法,就没有问题,因为在这种情况下,单参数重载会被认为是更好的方法。
如果可选参数从一开始就是C#的一部分,而不是派对的相对较晚的添加(有一些奇怪的实现,其中值在呼叫站点扩展),这可能已被考虑并以某种方式减轻。目前最明显的解决方法是通过实际更改base
查找的规则,使其更喜欢匹配与其出现的方法的签名完全匹配的方法,但这会使已经复杂的重载规则复杂化甚至更多,它肯定会破坏现有的代码,所以这种事情发生的可能性很小。
如果你不能改变A
,B
和C
,仍然有办法编写D
以获得所需的行为,通过利用(如果这个词)另一个相当模糊的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)
。