P D*_*ddy 4 c# extension-methods overload-resolution
我无法让编译器解决扩展方法的正确重载问题.我解释的最好方法是使用一些代码.这是一个演示问题的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();
}
}
Run Code Online (Sandbox Code Playgroud)
我得到的错误是:
以下方法或属性之间的调用不明确:'
Extensions.Foo<Container<A>>(Container<A>, System.Func<Container<A>,bool>)'和'Extensions.Foo<A>(Container<A>, System.Func<A,bool>)'
在我看来,它根本不含糊.第一种方法不接受a Container<T>,只接受a 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();
}
}
Run Code Online (Sandbox Code Playgroud)
这会编译并产生预期的结果:
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,它工作正常.当Foo使用基于lambda参数的返回值调用时,它可以正常工作.即使lambda的返回值与未编译的示例中的返回值相同,但lambda参数由虚拟赋值引用,它也可以工作.
为什么它在这些情况下有效而在第一种情况下不起作用?我做错了什么,或者我发现了编译器错误?我已经确认了C#4和C#6中的行为.
哦,我重新阅读了自己的答案后得到了它!好问题=)重载不起作用,因为它where T:IMaker在解析重载时没有考虑约束(约束不是方法签名的一部分).当您在lambda中引用参数时,您(可以)向编译器添加提示:
这有效:
new Container<A>().Foo(a => a.AProp == 0);
Run Code Online (Sandbox Code Playgroud)
因为在这里我们暗示a:A;
即使引用参数,这也不起作用:
new Container<A>().Foo(a => a != null);
Run Code Online (Sandbox Code Playgroud)
因为仍然没有足够的信息来推断出类型.
据我所知,在"Foo场景"中,推理可能会在第二个(Func)参数上失败,从而使调用变得模糊.
这是规范(25.6.4)所说的:
类型推断作为方法调用(第14.5.5.1节)的编译时处理的一部分发生,并在调用的重载解析步骤之前发生.如果在方法调用中指定了特定方法组,并且未将任何类型参数指定为方法调用的一部分,则会将类型推断应用于方法组中的每个泛型方法.如果类型推断成功,则推断的类型参数用于确定后续重载解析的参数类型.
如果重载决策选择泛型方法作为要调用的方法,则推断的类型参数将用作调用的运行时类型参数.如果特定方法的类型推断失败,则该方法不参与重载解析.类型推断的失败本身并不会导致编译时错误.但是,当重载解析无法找到任何适用的方法时,它通常会导致编译时错误.
现在让我们来看看非常简单的"酒吧情景".在类型推断之后,我们将只获得一个方法,因为只有一个方法适用:
Bar(Container<A>)for new Container<A>()(未实施IMaker)Bar(A)for new A()(不是容器)以下是ECMA-334规范,以防万一.Ps我不是100%肯定我做对了,但我更愿意认为我掌握了必不可少的部分.
| 归档时间: |
|
| 查看次数: |
381 次 |
| 最近记录: |