通用方法与非通用方法之间的C#重载解析

Xin*_*hao 5 c# generics overload-resolution

我在Internet和stackoverflow上进行了一些基本搜索,当涉及通用版本方法和非通用版本方法时,我看到了很多有关重载解析的讨论。我知道重载再分解是在编译时完成的-因此,如果我有以下代码:

public class A<T>
{
    public void DoStuff(T value)
    {
         InternalDoStuff(value);
    }

    protected void InternalDoStuff(int value)
    {
         Console.WriteLine("Non-generic version");
    }

    protected void InternalDoStuff(T value)
    {
         Console.WriteLine("Generic version");
    }

}

public class Test
{
    static void Main (string [] args)
    {
         A<int> a = new A<int> ();
         a.DoStuff(100);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出将是“通用版本”,因为编译器已经选择了“ InternalDoStuff”的分辨率,并且编译器看到的是“在DoStuff中使用T类型参数调用了InternalDoStuff”。

但是我不知道这是否会有所作为:

public class B : A <int> 
{

}

public class Test
{
    static void Main (string [] args)
    {
         B b = new B ();
         b.DoStuff(100);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以说编译器具有足够的信息来确定“ B是A的特定版本”,因此可以调用InternalDoStuff的非泛型版本吗?

是否有任何一般原则来分析此类过载解决方案?

Roh*_*ats 4

第二种方法在任何意义上与第一种方法没有什么不同。

从 A 派生类 B 绝不会改变为类 A 生成的 IL 代码。B 只是继承这些方法。

如果您查看 A 类的 IL 代码,您可以看到它被编译为调用通用版本而不是非通用版本 -

.method public hidebysig instance void DoStuff(!T 'value') cil managed
{
    .maxstack 8
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldarg.1 
    L_0003: call instance void
            ConsoleApplication1.A`1<!T>::InternalDoStuff(!0) <-- Generic version
    L_0008: nop 
    L_0009: ret 
}
Run Code Online (Sandbox Code Playgroud)

来自Jon Skeet 的文章-

提醒一下,当两个方法具有相同名称但签名不同时,就会发生重载。在编译时,编译器根据参数的编译时类型和方法调用的目标确定要调用哪一个。(我假设您在这里没有使用动态,这使事情变得有些复杂。)

正如他提到的,使用动态延迟解析直到运行时。这段代码将为您的两种方法调用非通用版本方法 -

public void DoStuff(T value)
{
   dynamic dynamicValue = value;
   InternalDoStuff(dynamicValue);
} 
Run Code Online (Sandbox Code Playgroud)

请参阅Jon SkeetEric Lippert的答案,详细描述。

  • “从 A 派生类 B 绝不会改变为类 A 生成的 IL 代码。B 只是继承了这些方法。” ——这是一个有说服力的观点 (3认同)