泛型重载.是预期行为吗?

本文关键字:重载 泛型 | 更新日期: 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)选择了方法。