我很难理解为什么在这个简单的例子中没有选择正确的过载。看看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>();
}
}
答案 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不同。)