- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我无法让编译器解析扩展方法的正确重载。我最好的解释方式是使用一些代码。这是一个 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();
}
}
我得到的错误是:
The call is ambiguous between the following methods or properties: '
Extensions.Foo<Container<A>>(Container<A>, System.Func<Container<A>,bool>)
' and '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>
中出现问题类(class)。调用Bar
时,没有 lambda,它工作正常。调用Foo
时使用基于 lambda 参数的返回值,它工作正常。即使 lambda 的返回值与未编译的示例中的返回值相同,但 lambda 参数由虚拟赋值引用,它仍然有效。
为什么它在这些情况下有效,但在第一个情况下却无效?我做错了什么,还是发现了编译器错误?我已经确认了 C# 4 和 C# 6 中的行为。
最佳答案
哦,我在重新阅读自己的答案后明白了!好问题 =)重载不起作用,因为它没有约束 where T:IMaker
在解决重载时考虑到(约束不是方法签名的一部分)。当您在 lambda 中引用参数时,您(可以)向编译器添加一个提示:
这个有效:
new Container<A>().Foo(a => a.AProp == 0);
因为这里我们确实暗示 a:A;
即使引用参数也不起作用:
new Container<A>().Foo(a => a != null);
因为仍然没有足够的信息来推断类型。
据我了解规范,在“Foo 场景”中,推理可能会在第二个 (Func) 参数上失败,从而使调用不明确。
这是规范 (25.6.4) 所说的:
Type inference occurs as part of the compile-time processing of a method invocation (§14.5.5.1) and takes place before the overload resolution step of the invocation. When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution.
If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the runtime type arguments for the invocation. If type inference for a particular method fails, that method does not participate in overload resolution. The failure of type inference, in and of itself, does not cause a compile-time error. However, it often leads to a compile-time error when overload resolution then fails to find any applicable methods.
现在让我们开始非常简单的“酒吧场景”。类型推断后我们将得到只有一个方法,因为只有一个适用:
Bar(Container<A>)
对于 new Container<A>()
(不实现 IMaker)Bar(A)
对于 new A()
(不是容器)这是 ECMA-334 specification , 以防万一。附言我不是 100% 确定我做对了,但我更愿意认为我掌握了本质部分。
关于c# - 奇怪的扩展方法重载决议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38578197/
tl;dr:我编写的函数创建了多个子进程,这些子进程在提交消息中的数据时解决 promise 。尽管该函数将所有这些 Promise 包装在 Promise.All 中,但该函数将突然返回,并且 Pr
我目前正在阅读 Jon Skeet 的 C# in depth 第 2 版,我想到了以下问题: 编译器如何能够在 list.Sort(Comparison) 之间进行选择?和 list.Sort(My
我是一名优秀的程序员,十分优秀!