泛型重载.是预期行为吗?
本文关键字:重载 泛型 | 更新日期: 2023-09-27 17:56:09
可能的重复项:
泛型方法重载的问题
下面是一个简单的代码:
static class Example
{
static int DoIt(object o) { return 0; }
class A { }
static int DoIt(A a) { return 1; }
static int CallDoIt<X>(X x) { return DoIt(x); }
static void Main()
{
var a = new A();
System.Console.WriteLine(DoIt(a)); // returns 1 (as desired)
System.Console.WriteLine(CallDoIt(a)); // returns 0
}
}
结果看起来很奇怪:直接调用的函数 DoIt() 返回的值与从另一个函数调用时的情况不同。这是 C# 中的预期行为吗?如果是,如何实现预期的行为(最好没有反思)?
预期的行为,X
是什么类型的知识不会扩展到CallDoIt
函数中。从CallDoIt
调用的DoIt
的重载是根据参数的类型静态确定的x
。由于X
可以是任何东西,因此最佳(也是唯一)候选人是 DoIt(object)
.
您可以通过使用 dynamic 将调度延迟到运行时DoIt
来绕过此行为:
static int CallDoIt<X>(X x) { return DoIt((dynamic)x); }
另一种选择是提供更具体的CallDoIt
版本:
static int CallDoIt(A a) { return DoIt(a); }
调用DoIt(x)
方法CallDoIt<X>(X x)
必须适用于任何X
类型,因此 C# 编译器选择使用DoIt(object o)
重载。如果将类型限制为从类型 A
派生X
如下所示:
static int CallDoIt<X>(X x) where X : A { return DoIt(x); }
然后 C# 编译器将知道它可以选择重载DoIt(A a)
因为X
类型将始终派生自类型 A
。这也回答了你的第二个问题,即如何实现预期的行为。
是的,这是预期的行为。泛型方法对所有可能的参数编译一次。在那个单一的编译中,DoIt(x)
不能解析为DoIt(A)
,所以它是DoIt(object)
的,这就是它永远的样子。
您可以动态检查对象类型,或者更好的是,让 .NET Framework 为您执行此操作:
static int CallDoIt(object x) { return DoIt((dynamic)x); }
请注意,在这里使CallDoIt
通用没有任何好处:它将与此版本完全相同。这意味着CallDoIt((object)a)
调用DoIt(A)
,而不是DoIt(object)
。
为 CallDoIt 添加另一个重载:
static int CallDoIt(A x) { return DoIt(x); }
这将使事情顺利进行。
重载解析发生在编译时(如果不需要动态解析),X
的最特定类型(类型参数)是object
。
选择调用方法的决定将在编译时解决。C# 编译器不知道 X 的类型是什么,并使用 DoIt(对象 o)选择了方法。