英文:
Why can't the compiler see IEnumerable<T> when IReadonlyDictionary<U, T> is also present
问题
Sure, here is the translated code portion without any additional content:
我有一个在.NET Framework 4.8中的类,它同时实现了`IEnumerable<T>`和`IReadOnlyDictionary<U, T>`:
```csharp
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' does not contain a definition for 'Any' and no accessible extension method 'Any' accepting a first argument of type 'Foo' could be found (are you missing a using directive or an assembly reference?)
我的代码中已经包含了using System.Linq
,如果我从类中删除IReadOnlyDictionary<int, Bar>
,错误就消失了。为什么编译器不能看到IEnumerable<T>
,当IReadOnlyDictionary<U, T>
也存在时?
<details>
<summary>英文:</summary>
I have a class in .NET Framework 4.8 that implements both `IEnumerable<T>` and `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()
{
/* ... */;
}
}
Using this class in a `foreach` loop works fine:
var foo = new Foo();
foreach (var bar in foo)
{
/* ... */
}
But if I try to use an extention method for `IEnumerable<T>`, for example
bool b = foo.Any();
I'm getting the error
> CS1061 'Foo' does not contain a definition for 'Any' and no accessible extension method 'Any' accepting a first argument of type 'Foo' could be found (are you missing a using directive or an assembly reference?)
My code already contains `using System.Linq`, and If I remove `IReadOnlyDictionary<int, Bar>` from my class the error goes away. Why can't the compiler see `IEnumerable<T>` when `IReadonlyDictionary<U, T>` is also present?
</details>
# 答案1
**得分**: 4
The compiler doesn't know whether you want to call `Any()` on:
* `IEnumerable<Bar>`, or
* `IReadOnlyDictionary<int, Bar> : IEnumerable<<KeyValuePair<int, Bar>>>`
So be explicit:
```csharp
IEnumerable<Bar> foo = ...
// or
IReadOnlyDictionary<int, Bar> foo = ...
if (foo.Any())
{
// ...
}
But yeah, the compiler error looks confusing, it could be clearer about that ambiguity.
英文:
The compiler doesn't know whether you want to call Any()
on:
IEnumerable<Bar>
, orIReadOnlyDictionary<int, Bar> : IEnumerable<<KeyValuePair<int, Bar>>
So be explicit:
IEnumerable<Bar> foo = ...
// or
IReadOnlyDictionary<int, Bar> foo = ...
if (foo.Any())
{
// ...
}
But yeah, the compiler error looks confusing, it could be clearer about that ambiguity.
答案2
得分: 4
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
也无法确定您想要什么。按照语言规范中指定的编译器执行步骤,了解这是如何工作的。
除了将其转换为所需的 IEnumerable<T>
实例(就像在 CodeCaster 的答案中一样),您还可以通过指定 Any
的类型参数告诉编译器您想要什么 TSource
:
bool b = foo.Any<Bar>();
英文:
Since IReadOnlyDictionary<K, V>
inherits from IEnumerable<KeyValuePair<K, V>>
. Your class implements both IEnumerable<KeyValuePair<int, Bar>>
and IEnumerable<Bar>
.
The extension method IEnumerable.Any
is generic, and is an extension on all IEnumerable<T>
s.
public static bool Any<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);
The compiler cannot figure out what TSource
should be - whether it should be Bar
or KeyValuePair<int, Bar>
.
foreach
doesn't have this issue only because you implemented IEnumerable<Bar>
implicitly. This allows GetEnumerator
to be found directly on Foo
when the compiler does a member lookup. If you had implemented it explicitly like this:
IEnumerator<Bar> IEnumerable<Bar>.GetEnumerator() => ...;
foreach
wouldn't be able to figure out what you want either. Follow the steps that the compiler takes, specified in the language spec to see exactly how this works.
In addition to casting it to the desired instantiation of IEnumerable<T>
like in CodeCaster's answer, you can also tell it what TSource
you want by specifying the type arguments of Any
:
bool b = foo.Any<Bar>();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论