Reputation: 1678
I am trying to understand IEnumerable interface how it works but finding it difficult to understand the concept because some examples use IEnumerator, GetEnumerator, Current, moveNext, Reset and some examples demonstrate IEnumerable with IEnumerator without Current,moveNext, Reset with nested inner class.
I need one simple example to understand this concept and when it's appropriate to use IEnumerator with Current, moveNext,Reset and when not.
Upvotes: 2
Views: 1434
Reputation: 6491
PROVIDER ========
Here an example where i've splitted the class from the enumerator. MyEnumerableClass is Enumerable so if someone needs to enumerate it, it can retrieve an enumerator on the class.
using System;
using System.Collections;
class MyEnumerableClass : IEnumerable
{
public IEnumerator GetEnumerator()
{
return new MyEnumerableClassEnumerator();
}
}
The enumerator is responsible to enumerate the elements on the class. In this example, I did not this, I hope is more clear in this way. In this example the class MyEnumerableClass and the enumerator on that class (MyEnumerableClassEnumerator) are not related. Usually the enumerator enumerates something contained in the class so the enumerator keeps a reference to the class. Also, if the state of the class changes "significantly", the enumerator could not enumerate it so it should raise InvalidOperationException. This behaviour usually is achieved adding a version identifier (i.e. int versionId) in the class. When the enumerator is istantiated it saves the versionId. If on a MoveNext call the enumerator sees that the versionId has changed it raises the exception. In this way you can have several enumerators active on the same class at the same time. Anyway, here the very simple example of an enumerator
class MyEnumerableClassEnumerator : IEnumerator
{
int myStatusVariable = 0;
public object Current
{
get { return myStatusVariable; }
}
public bool MoveNext()
{
return ++myStatusVariable < 10;
}
public void Reset()
{
myStatusVariable = 0;
}
}
CONSUMERS ======== Now the consumers. You can consume an anumerator in 2 ways, via Language (that generates interface calls) or generating calls by yourself
Here the examples based on the class above
MyEnumerableClass myEnumerableClass = new MyEnumerableClass();
foreach (var item in myEnumerableClass)
{
Console.WriteLine(item);
}
IEnumerator enumerator = myEnumerableClass.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
Upvotes: 0
Reputation: 1062780
It is virtually never appropriate to use Reset()
, since that method is explicitly not reliable: iterator blocks do not support it, for example. So there is very little point worrying about implementing it.
If you mean in terms of implementing it: most of the time, the most appropriate thing to do is to use an iterator block (aka yield return
), because doing this by hand is hard, error-prone, and usually serves no great purpose. You would need to have something very specific in mind to warrant implementing an enumerator by hand.
Some examples you see may pre-date when iterator blocks were addd to the language; in which case, they had no option except to write an iterator by hand.
Most times: just let the compiler worry about the heavy lifting. yield return
is your friend. Note: things like List<T>
use a custom iterator that is value-typed etc to minimize impact; note that when you foreach
over a List<T>
you aren't even actually using IEnumerable<T>
- you are just using the raw GetEnumerator()
and associated APIs. foreach
can use IEnumerable<T>
, but it does not require it.
Upvotes: 3