我正在构建游戏的源代码,并尝试使用重载方法和默认参数调用基本虚拟函数。我无法更改派生的类,需要在我自己的虚拟方法的类定义中调用该函数。我将尝试用代码更详细地解释。
首先,我们有一个基类A,它定义了一个称为Foo的虚拟函数,该函数带有一个参数。
class A
{
public virtual string Foo(int a)
{
return "Class A Function 1 par";
}
}
然后是一个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";
}
}
然后,我需要派生的类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”)。我认为这种带有默认参数的虚拟函数重载会导致模棱两可的编译器错误,但编译良好。
是否有一种方法可以解决此问题,如果不能解决,为什么它允许类的结构将我锁定为无法访问基本方法?
答案 0 :(得分:3)
这是可选参数与语言其余部分之间的不幸交互,这是为什么您基本上不应该重载使用可选参数的方法(或对不使用此方法的方法添加重载)的一个很好的理由。 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)
不是有效的候选者,这迫使我们进一步向上寻找{{1 }}。