我在。net Framework 4.8中有一个类实现了IEnumerable<T>
和IReadonlyDictionary<U, T>
:
public class Foo : IEnumerable<Bar>, IReadOnlyDictionary<int, Bar>
{
public Bar this[int key] => /* ... */;
public IEnumerable<int> Keys => /* ... */;
public IEnumerable<Bar> Values => /* ... */;
public int Count => /* ... */;
public bool ContainsKey(int key) => /* ... */;
public IEnumerator<Bar> GetEnumerator() => /* ... */;
public bool TryGetValue(int key, out Bar value) => /* ... */;
IEnumerator IEnumerable.GetEnumerator() { /* ... */; }
IEnumerator<KeyValuePair<int, Bar>> IEnumerable<KeyValuePair<int, Bar>>.GetEnumerator()
{
/* ... */;
}
}
在foreach
循环中使用这个类工作得很好:
var foo = new Foo();
foreach (var bar in foo)
{
/* ... */
}
但是如果我尝试使用IEnumerable<T>
的扩展方法,例如
bool b = foo.Any();
我得到错误
CS1061 'Foo'不包含'Any'的定义,也没有可访问的扩展方法'Any'接受'Foo'类型的第一个参数可以找到(您是否缺少using指令或程序集引用?)
我的代码已经包含using System.Linq
,如果我从我的类中删除IReadOnlyDictionary<int, Bar>
,错误就会消失。当IReadonlyDictionary<U, T>
也存在时,为什么编译器不能看到IEnumerable<T>
?
由于IReadOnlyDictionary<K, V>
继承自IEnumerable<KeyValuePair<K, V>>
。你的类实现了IEnumerable<KeyValuePair<int, Bar>>
和IEnumerable<Bar>
。
扩展方法IEnumerable.Any
是通用的,并且是所有IEnumerable<T>
的扩展。
public static bool Any<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);
编译器不知道TSource
应该是什么——应该是Bar
还是KeyValuePair<int, Bar>
。
foreach
没有这个问题,只是因为你隐式地实现了IEnumerable<Bar>
。这允许在编译器进行成员查找时直接在Foo
上找到GetEnumerator
。如果您像这样显式地实现它:
IEnumerator<Bar> IEnumerable<Bar>.GetEnumerator() => ...;
foreach
也不会知道你想要什么。按照语言规范中指定的编译器所执行的步骤,查看它是如何工作的。
除了像CodeCaster的答案那样将其强制转换为所需的IEnumerable<T>
实例化之外,您还可以通过指定Any
的类型参数来告诉它您想要的TSource
:
bool b = foo.Any<Bar>();
编译器不知道你是否想要调用Any()
on:
IEnumerable<Bar>
或IReadOnlyDictionary<int, Bar> : IEnumerable<<KeyValuePair<int, Bar>>
所以要明确:
IEnumerable<Bar> foo = ...
// or
IReadOnlyDictionary<int, Bar> foo = ...
if (foo.Any())
{
// ...
}
但是,是的,编译器错误看起来很混乱,可以更清楚地说明这种模糊性