在下面的代码中,我定义了两个名为Bar
的重载方法。在Foo()
中,我对Bar
进行三次调用,并在第三次调用时收到错误。
前两个解决了预期的重载问题。然而第三个产生了以下错误:
The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Bar<T>(T, string, params object[])'. There is no implicit reference conversion from 'string' to 'System.Exception'.
显然,第三个调用绑定到“Bar()”,但未能将第一个参数转换为Exception。很明显,它是第二个参数,它是一个抛弃编译器的字符串。当它不是一个字符串(案例2)时,分辨率很好。但似乎很明显(对我来说)失败的行应该绑定到“Bar()”(因为第一个参数明确地是一个字符串)。
有人能解释为什么编译器正在使用这种绑定吗?我会考虑创造性的解决方法,但我真正想要的是解释为什么会这样。我花了一些时间使用C#语言规范,但未能找到(即放弃)一个明确的答案。显然,我可以重命名其中一个Bar
方法,或者提供命名参数,或者标记其中一个参数ref
......但是这些参数都不适用于我的特定场景。
不是说它是相关的,但是我编写的Java代码就是这样做的,并且编译器没有问题。
代码:
public void Bar(string s, params object[] ps) { } // Call this "Bar()"
public void Bar<E>(E e, string s, params object[] ps) where E : Exception { } // Call this "Bar<T>()"
public void Foo()
{
Exception e;
Object o1, o2;
Bar(e, "fmt {0}", o); // Resolves fine, as expected
Bar("fmt {0} {2}", o1, o2); // Also resolves as expected
Bar("fmt {0} {2}", "bar", o1); // Error!
}
答案 0 :(得分:4)
答案是过载匹配不考虑约束。至于这是C#规范,我不完全确定。匹配始终使用最具体的选项和字符串,因为泛型T
总是比字符串更具体Object
(因为它匹配实际类型而不是子类型)。请参阅Eric Lippert博客上的Constraints are not part of the signature。
要使其工作,如果需要约束,请尽可能使用void Bar(Exception E, ...)
。
答案 1 :(得分:2)
Bar("fmt {0} {2}", "bar", o1);
//错误!
通过特异性优先级,它寻找方法签名,其中第二个参数是字符串(字符串比对象更具体),然后尝试解析Type参数,但它不能。为了说明它,尝试将第三个调用替换为:
Bar("fmt {0} {2}", (object)"bar", o1); // Now it is fine for compiler!