为什么在非泛型存在时选择通用方法?

时间:2015-06-30 21:59:07

标签: c# generics overloading overload-resolution

以下程序产生此输出:

Foo<T> called

Process is terminated due to StackOverflowException.

因此,Foo(baz)会调用通用Foo<T>,但Bar(baz)会递归并调用Bar<T>

我在C#5.0和Microsoft .NET上。当非泛型方法是override时,编译器似乎选择泛型方法而不是递归。

我在哪里可以找到有关此规则的解释?(我猜测编译器会在两种情况下选择递归。)

以下是整个计划:

using System;

namespace ConsoleApplication1 {
    class Baz { }

    abstract class Parent {
        public abstract void Foo(Baz baz);
    }

    class Child : Parent {
        void Bar<T>(T baz) {
            Console.WriteLine("Bar<T> called");
        }

        public void Bar(Baz baz) {
            Bar(baz);
        }

        void Foo<T>(T baz) {
            Console.WriteLine("Foo<T> called");
        }

        public override void Foo(Baz baz) {
            Foo(baz);
        }
    }

    class Program {
        static void Main(string[] args) {
            var child = new Child();
            child.Foo(null);
            child.Bar(null);
            Console.ReadLine();
        }
    }
}

3 个答案:

答案 0 :(得分:5)

根据MSDN文档,优先考虑未被覆盖的方法签名。由于Foo的非泛型版本被覆盖,因此它会立即进入选择方法的优先级的底部。一般而言,下一步是选择最具体的方法并执行它。对于Bar方法,Bar(Baz baz)方法在您的情况下将始终是最具体的。

  

重载解析是一种选择最佳解决方案的编译时机制   函数成员调用给定的参数列表和一组   候选职能成员。重载分辨率选择该功能   要在C#中的以下不同上下文中调用的成员:

     
      
  • 调用invocation-expression中指定的方法(Section   7.5.5)。调用在object-creation-expression中命名的实例构造函数(第7.5.10.1节)。
  •   
  • 通过元素访问调用索引器访问器(第7.5.6节)。调用表达式中引用的预定义或用户定义的运算符   (第7.2.3节和第7.2.4节)。
  •   
     

这些背景中的每一个都定义了   候选函数成员集和自己的参数列表   独特的方式,如上面列出的部分中详细描述的那样。 :用于   例如,方法调用的候选集不会   包括标记为覆盖的方法(第7.3节)和基础中的方法   如果派生类中的任何方法是,则class不是候选者   适用(第7.5.5.1节)。

MSDN Overload Resolution

我把我认为与你的问题有关的文字加粗了。

这是关于Stack Overflow的另一个问题,可能会有所帮助。它总体上讨论了方法解决方案。不涉及重写方法,但有助于填写我没有触及的一些过程。

答案 1 :(得分:1)

重载分辨率搜索继承链,在每个点查找定义的方法。

Child定义void Foo<T>(T baz)但未定义void Foo(Baz baz),因此选择了void Foo<T>(T baz)

一般来说,这是有道理的;在实际代码中如果Foo<T>(T baz)Foo(Baz baz)传递Baz的基础public new void Foo(Baz baz)没有做同样的工作,那么您的设计会让人感到困惑,您应该选择一个新名称。< / p>

您可以使用public new virtual void Foo(Baz baz)Child来强制进行覆盖,以强制在base.Foo(baz)中定义覆盖(尽管此处需要进行中间步骤)层次结构,以便抽象方法有一个实现),可以调用Foo<Baz>(baz)(调用基本实现)和/或> network_matrix login mentions weight [1,] "rtomayko" "author" "1" > str(network_matrix) chr [1, 1:3] "rtomayko" "author" "1" - attr(*, "dimnames")=List of 2 ..$ : NULL ..$ : chr [1:3] "login" "mentions" "weight" > typeof(network_matrix) [1] "character" > g = graph.edgelist(network_matrix[,1:2], directed = TRUE) Error in graph.edgelist(network_matrix[, 1:2], directed = TRUE) : graph_from_edgelist expects a matrix with two columns > (调用泛型版)`,但最好避免这样的技巧

答案 2 :(得分:0)

也许它就像你实现类似的东西一样

    void myMethod(long? l) { }
    void myMethod(int? i) { }

使用null调用它将使用int?

添加这个

    void myMethod(short? i) { }

仍然使用null调用它,代码将切换为short?

可能正在进行内部订单/优先级?

现在使用您的代码,我只是为了表明让编译器决定和程序员决定(显式调用)之间的区别

这是通用Baz

.method private hidebysig 
    instance void Bar<T> (
        !!T baz
    ) cil managed 
{
    // Method begins at RVA 0x2060
    // Code size 13 (0xd)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldstr "Bar<T> called"
    IL_0006: call void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop
    IL_000c: ret
} // end of method Child::Bar

您的实施

    public void Bar(Baz baz) {
        Bar(baz);
    }

给这个

.method public hidebysig 
    instance void Bar (
        class ConsoleApplication1.Baz baz
    ) cil managed 
{
    // Method begins at RVA 0x206e
    // Code size 10 (0xa)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: call instance void ConsoleApplication1.Child::Bar(class ConsoleApplication1.Baz)
    IL_0008: nop
    IL_0009: ret
} // end of method Child::Bar

这一个

    public void Bar(Baz baz)
    {
        Bar<Baz>(baz);
    }

给这个

.method public hidebysig 
    instance void Bar (
        class ConsoleApplication1.Baz baz
    ) cil managed 
{
    // Method begins at RVA 0x206e
    // Code size 10 (0xa)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: call instance void ConsoleApplication1.Child::Bar<class ConsoleApplication1.Baz>(!!0)
    IL_0008: nop
    IL_0009: ret
} // end of method Child::Bar