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?
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>();