为什么编译器在<T>IReadonlyDictionary<U,T>也存在时看不到IEnumerable



我在。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())
{
// ...
}

但是,是的,编译器错误看起来很混乱,可以更清楚地说明这种模糊性

最新更新