VS 2015方法重载解析行为

时间:2015-08-07 15:18:56

标签: c# generics .net-4.5 visual-studio-2015 overloading

我很难理解为什么在这个简单的例子中没有选择正确的过载。看看7.5.3.2更好的函数成员下的C# 5 spec,它似乎应该能够选择非泛型重载,但object参数似乎在某种程度上影响了我的决定#39} ;我不理解。我遇到的问题是,如果不将参数强制转换为Foo(object),我就无法调用object的非泛型版本。从错误来看,它似乎不受支持,我希望有人可以了解原因。

public class A
{
    public string Type { get { return "non-generic"; } }
}

public class A<T>
{
    public string Type { get { return "generic"; } }
}

class Program
{
    // (In reality only one of the variants below can be uncommented.)
    static void Main(string[] args)
    {
        // This works fine and calls the generic overload as expected
        A<string> x = Foo<string>("foobar");

        // This results in a compile time error
        // CS0029: Cannot implicitly convert type 'A<string>' to 'A'
        A x = Foo("foobar");

        // This works, but ends up calling the generic overload
        var x = Foo("foobar");

        // This works fine and calls the non-generic overload as expected
        object a = "foobar";
        var x = Foo(a);

        // This works fine and calls the non-generic overload as expected
        A x = Foo((object)"foobar");

        // By using dynamic we're able to get rid of the compile-time error, but get a
        // runtime exception.
        // RuntimeBinderException: Cannot implicitly convert type 'A<string>' to 'A'
        A x = Foo((dynamic)"foobar");

        Console.WriteLine(x.Type);
        Console.ReadLine();
    }

    private static A Foo(object x)
    {
        return new A();
    }

    private static A<T> Foo<T>(T x)
    {
        return new A<T>();
    }
}

1 个答案:

答案 0 :(得分:2)

A x = Foo("foobar");

C#选择泛型方法,因为它比非泛型方法更具体,不需要转换。实际上,C#编译器会创建Foo方法的副本,并使用具体类型T替换泛型类型参数string。重载决策在编译时执行。在运行时,将调用带有字符串参数的方法。在运行时不会创建通用开销。

请注意,只有分配右侧的表达式才会被考虑在内。更具体地说,C#查看方法签名,即方法参数。 not 属于其签名的方法的返回类型。

泛型方法返回A<T>,但由于A<T>不是来自A,因此方法A<T>的类型Foo<T>()的结果不能已分配给x,其类型为A。对于动态示例也是如此:没有从A<T>A的有效转换。由于重载解析是在编译时完成的,因此动态无法解决您的问题。动力正在做他们的工作&#34; (即绑定)在运行时。

同样,它不是您期望从确定使用哪个重载的方法的结果,而是传递给此方法的(静态)参数。

另一个有助于澄清事实的例子:

var x = Foo(5);
var y = Foo("hello");

在编译时,C#创建了两个Foo方法的副本!一个int,一个string代替泛型类型参数T。在运行时,不会发生转换;甚至不是拳击(与将int包装成对象的Java不同。)