I am making collection, implementing IEnumerable
explicitly and trying to iterate it from within:
public class MyCollection<T> : IEnumerable<T>, IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator<T> GetEnumerator() { yield return default(T); } // test
public void Test()
{
foreach (var item in this) { } // here is warning
}
}
I get compiler warning at this
:
Warning CS0279 'MyCollection' does not implement the 'collection' pattern. 'MyCollection.GetEnumerator()' is either static or not public.
Hell yes, it's not public. Why it should be? I can make it public, but it's not needed for foreach
outside of type:
foreach (var item in new MyCollection<string>()) { } // no warning
Am I doing something wrong?
The warning exists because the C# compiler can handle foreach
in a number of different ways. One of those ways is to find a GetEnumerator
method with a suitable return type. That's checked before the compiler checks whether or not the type of the expression implements IEnumerable
or IEnumerable<T>
.
In your case, it gets as far as finding the single parameterless GetEnumerator
method, but it's not public. The C# specification recommends a warning at this point, as you may well have intended it to be usable for foreach
. From the C# 5 spec, section 8.8.4, emphasis mine:
- Perform overload resolution using the resulting method group and an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below. It is recommended that a warning be issued if overload resolution produces anything except an unambiguous public instance method or no applicable methods.
Any of the following would solve the problem:
Rename GetEnumerator
to GetEnumeratorImpl
or similar:
IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorImpl();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumeratorImpl();
IEnumerator<T> GetEnumeratorImpl() { yield return default(T); }
Don't use explicit interface implementation for IEnumerable<T>.GetEnumerator()
- put the implementation there
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<T> GetEnumerator() => { yield return default(T); }
Put the implementation in IEnumerable<T>.GetEnumerator
, but cast this
to IEnumerable<T>
in IEnumerable.GetEnumerator
to call it:
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>) this).GetEnumerator();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => { yield return default(T); }
See more on this question at Stackoverflow