VS 2015 方法重载解析行为
VS 2015 Method Overload Resolution Behavior
我很难理解为什么在这个简单的例子中没有select正确的重载。查看7.5.3.2下的C# 5 spec更好的函数成员,似乎它应该能够select非泛型重载,但object
参数似乎以某种方式参与决定我不明白。我遇到的问题是,如果不将参数转换为 object
,我就无法调用 Foo(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>();
}
}
在
A x = Foo("foobar");
C# 选择泛型方法,因为它比非泛型方法更具体并且不需要转换。事实上,C# 编译器创建了 Foo 方法的副本,并用具体类型 string
替换了泛型类型参数 T
。重载决议在编译时执行。在 运行 时,将调用一个带有字符串参数的方法。 运行 时没有创建通用开销。
请注意,解析时仅考虑赋值右侧的表达式。更具体地说,C# 查看方法签名,即方法参数。 return 类型的方法不 属于它的签名。
泛型方法 return 是一个 A<T>
,但是由于 A<T>
不是从 A
派生的,所以方法的类型 A<T>
的结果Foo<T>()
无法分配给类型为 A
的 x
。对于 dynamic 的示例也是如此:没有从 A<T>
到 A
的有效转换。由于重载决议是在编译时完成的,动态无法解决您的问题。 Dynamics 在 运行 时间进行 "job"(即绑定)。
同样,确定使用哪个重载的方法不是您期望的结果,而是传递给此方法的(静态)参数。
再举例说明真相:
var x = Foo(5);
var y = Foo("hello");
在编译时,C# 创建了 Foo 方法的两个副本!一种使用 int
,另一种使用 string
代替泛型类型参数 T
。在 运行 时间没有发生转换;甚至没有装箱(不像 Java 那样会将 int
包装到一个对象中)。
我很难理解为什么在这个简单的例子中没有select正确的重载。查看7.5.3.2下的C# 5 spec更好的函数成员,似乎它应该能够select非泛型重载,但object
参数似乎以某种方式参与决定我不明白。我遇到的问题是,如果不将参数转换为 object
,我就无法调用 Foo(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>();
}
}
在
A x = Foo("foobar");
C# 选择泛型方法,因为它比非泛型方法更具体并且不需要转换。事实上,C# 编译器创建了 Foo 方法的副本,并用具体类型 string
替换了泛型类型参数 T
。重载决议在编译时执行。在 运行 时,将调用一个带有字符串参数的方法。 运行 时没有创建通用开销。
请注意,解析时仅考虑赋值右侧的表达式。更具体地说,C# 查看方法签名,即方法参数。 return 类型的方法不 属于它的签名。
泛型方法 return 是一个 A<T>
,但是由于 A<T>
不是从 A
派生的,所以方法的类型 A<T>
的结果Foo<T>()
无法分配给类型为 A
的 x
。对于 dynamic 的示例也是如此:没有从 A<T>
到 A
的有效转换。由于重载决议是在编译时完成的,动态无法解决您的问题。 Dynamics 在 运行 时间进行 "job"(即绑定)。
同样,确定使用哪个重载的方法不是您期望的结果,而是传递给此方法的(静态)参数。
再举例说明真相:
var x = Foo(5);
var y = Foo("hello");
在编译时,C# 创建了 Foo 方法的两个副本!一种使用 int
,另一种使用 string
代替泛型类型参数 T
。在 运行 时间没有发生转换;甚至没有装箱(不像 Java 那样会将 int
包装到一个对象中)。