【问题标题】:overloading with generics. Is it expected behaviour? [duplicate]用泛型重载。这是预期的行为吗? [复制]
【发布时间】:2013-01-28 04:54:53
【问题描述】:

可能重复:
A problem with generic method overloading

这是一个简单的代码:

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# 中的预期行为吗?如果是,如何实现所需的行为(最好没有反射)?

【问题讨论】:

    标签: c#


    【解决方案1】:

    这是预期的行为,X 是什么类型的知识不会扩展到 CallDoIt 函数中。从CallDoIt 调用的DoIt 的重载是根据参数x 的类型静态确定的。因为X 可以是任何东西,所以最好的(也是唯一的)候选人是DoIt(object)

    您可以通过使用动态将调度延迟到DoIt 直到运行时来解决此问题:

    static int CallDoIt<X>(X x) { return DoIt((dynamic)x); }
    

    另一种选择是提供更具体的CallDoIt 版本:

    static int CallDoIt(A a) { return DoIt(a); }
    

    【讨论】:

      【解决方案2】:

      方法DoIt(x) 内部的方法调用CallDoIt&lt;X&gt;(X x) 必须适用于任何X 类型,因此C# 编译器选择使用DoIt(object o) 重载。 如果您限制X 类型从A 类型派生,如下所示:

      static int CallDoIt<X>(X x) where X : A { return DoIt(x); }
      

      然后 C# 编译器将知道它可以选择 DoIt(A a) 重载,因为 X 类型将始终派生自 A 类型。这也回答了您关于如何实现所需行为的第二个问题。

      【讨论】:

        【解决方案3】:

        是的,这是预期的行为。为所有可能的参数编译一次泛型方法。在单次编译期间,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)

        【讨论】:

        • 感谢您的回答。使用动态确实可以解决问题。我担心这种 C# 行为容易出错。
        • 在更复杂的情况下,在编写方法 CallDoIt() 时,可能对 DoIt() 的实现不感兴趣(事实上,这个实现可能稍后会更改)。似乎从泛型调用重载方法通常是危险的。对吗?
        • @user2016189 这是 C# 泛型和 C++ 模板之间的根本区别之一。在 C++ 模板中,您的代码的等效项将调用 DoIt(A) 重载。我不知道你是否熟悉将 C++ 模板放入库中的问题,但如果你不熟悉,泛型解决了一个大问题,带来了相当小的不便,因为事实证明,需要模板专业化 /过载相对较低。
        • @user2016189 我猜这是一个设计选择:如果DoIt((object)a)DoIt(a) 做不同的事情,也许他们不应该有相同的名字?如果这些函数的名称不同,那么您不会处理重载函数,也不会遇到现在遇到的问题。
        • 还要注意,这种“错误”在编译和运行时都不会被发现。
        【解决方案4】:

        为 CallDoIt 添加另一个重载:

        static int CallDoIt(A x) { return DoIt(x); }
        

        这会让事情顺利进行。

        【讨论】:

          【解决方案5】:

          重载解析发生在编译时(如果不需要动态解析),X(类型参数)的最具体类型是object

          【讨论】:

            【解决方案6】:

            选择调用方法的决定将在编译时解决。 C#编译器不知道X是什么类型,选择了DoIt(object o)的方法。

            【讨论】:

              猜你喜欢
              • 2020-07-28
              • 1970-01-01
              • 2017-03-12
              • 2019-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多