我在编译器中遇到了一个问题,无法解析扩展方法的正确重载。最好的方法是通过一些代码来解释。这里有一个LINQPad脚本来演示这个问题。由于我遇到的问题,这段代码无法编译:
void Main(){
new Container<A>().Foo(a=>false);
}
interface IMarker{}
class A : IMarker{
public int AProp{get;set;}
}
class B : IMarker{
public int BProp{get;set;}
}
class Container<T>{}
static class Extensions{
public static void Foo<T>(this T t, Func<T, bool> func)
where T : IMarker{
string.Format("Foo({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Foo<T>(this Container<T> t, Func<T, bool> func){
string.Format("Foo(Container<{0}>)", typeof(T).Name).Dump();
}
}
我收到的错误信息是:
调用 '
Extensions.Foo<Container<A>>(Container<A>, System.Func<Container<A>,bool>)
' 和 'Extensions.Foo<A>(Container<A>, System.Func<A,bool>)
' 方法或属性不明确。对我来说,这根本不是模棱两可的。第一个方法不接受
Container<T>
,只接受 IMarker
。看起来泛型约束并没有帮助解决重载冲突,但在这个版本的代码中,它们似乎做到了。void Main(){
new A().Bar();
new A().Foo(a=>a.AProp == 0);
new A().Foo(a=>false); // even this works
new A().Foo(a=>{
var x = a.AProp + 1;
return false;
});
new Container<A>().Bar();
new Container<A>().Foo(a=>a.AProp == 0);
new Container<A>().Foo(a=>{
var x = a.AProp + 1;
return false;
});
}
interface IMarker{}
class A : IMarker{
public int AProp{get;set;}
}
class B : IMarker{
public int BProp{get;set;}
}
class Container<T>{}
static class Extensions{
public static void Foo<T>(this T t, Func<T, bool> func)
where T : IMarker{
string.Format("Foo({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Foo<T>(this Container<T> t, Func<T, bool> func){
string.Format("Foo(Container<{0}>)", typeof(T).Name).Dump();
}
public static void Bar<T>(this T t) where T : IMarker{
string.Format("Bar({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Bar<T>(this Container<T> t){
string.Format("Bar(Container<{0}>)", typeof(T).Name).Dump();
}
}
这段代码编译并输出了预期的结果:
Bar(A:IMarker)
Foo(A:IMarker)
Foo(A:IMarker)
Foo(A:IMarker)
Bar(Container<A>)
Foo(Container<A>)
Foo(Container<A>)
只有在lambda表达式中不引用lambda参数,并且仅涉及Container<T>
类时,才会出现问题。当调用Bar
时,没有lambda表达式,因此可以正常工作。当以基于lambda参数的返回值调用Foo
时,它也可以正常工作。即使lambda的返回值与未编译的示例中的返回值相同,但是通过一个虚拟赋值来引用lambda参数,它也可以正常工作。
为什么它们能正常工作而第一种情况不能?我做错了什么,还是我发现了编译器的错误?我已经在C# 4和C# 6中确认了这种行为。