我'm having trouble getting the compiler to resolve the correct overload for an extension method. The best way for me to explain is with a little code. Here'是一个演示问题的LINQPad脚本 . 这赢得了't compile because of the problem I' m:
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>(容器<A>,System.Func <A,bool>)'
在我看来它's not ambiguous at all. The first method won' t接受 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 's return value is the same as the one in the example that doesn't编译,但lambda参数由伪赋值引用,它也可以工作 .
为什么它在这些情况下有效而在第一种情况下不起作用?我做错了什么,或者我发现了编译器错误?我已经确认了C#4和C#6中的行为 .
2 回答
哦,我重新阅读了自己的答案后得到了它!好的问题=)重载不起作用,因为它在解析重载时不考虑约束
where T:IMaker
(约束不是方法签名的一部分) . 当您在lambda中引用参数时,您(可以)向编译器添加提示:因为在这里我们暗示a:A;
因为仍然没有足够的信息来推断出类型 .
据我所知,在“Foo场景”中,推理可能会在第二个(Func)参数上失败,从而使调用变得模糊 .
这是规范(25.6.4)所说的:
现在让我们来看看非常简单"Bar scenario" . 在类型推断之后,我们将获得 only one 方法,因为只有一个适用:
Bar(Container<A>)
fornew Container<A>()
(未实施IMaker)Bar(A)
fornew A()
(不是容器)这是ECMA-334 specification,以防万一 . 附:我不是百分百肯定我做对了,但我更倾向于认为我掌握了必不可少的部分 .
Sergey弄清楚为什么我试图做的事情并没有决定改为:
对我来说不幸的是,这对我的应用来说是一个很大的改变 . 但至少扩展层会更简单,最终它对于编译器和人类来说都不那么模糊,这是一件好事 .