I have a class that stores a string list, I would like to make this class usable in a foreach statement, so I found these two interfaces and I tried to implement them.
public class GroupCollection : IEnumerable, IEnumerator
{
public List<string> Groups { get; set; }
public int Count { get { return Groups.Count; } }
int position = -1;
}
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
public object Current
{
get
{
try
{
return new Group(Groups[position]);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public bool MoveNext()
{
position++;
return position < Groups.Count;
}
public void Reset()
{
position = 0;
}
I'm iterating through a GroupCollection variable twice:
foreach (GroupCollection.Group in groups) // where groups is a GroupCollection
{
}
foreach (GroupCollection.Group in groups)
{
}
// where Group is a nested class in GroupCollection.
When it is at the first foreach it works well (count is 1 at this time). I don't modify anything, and when it goes to the second foreach it doesn't go into the loop. I went through the code line by line in debugging mode and found out that the reset is not called after the first foreach. So should I manually call reset after the foreach? Isn't there a nicer way to do this?
I don't modify anything
Yes you do - your MoveNext()
modifies the state of the class. This is why you shouldn't implement both IEnumerable
and IEnumerator
in the same class. (The C# compiler does for iterator blocks, but that's a special case.) You should be able to call GetEnumerator()
twice and get two entirely independent iterators. For example:
foreach (var x in collection)
{
foreach (var y in collection)
{
Console.WriteLine("{0}, {1}", x, y);
}
}
... should give you all possible pairs of items in a collection. But that only works when the iterators are independent.
I went through the code line by line in debugging mode and found out that the reset is not called after the first foreach.
Why would you expect it to? I don't believe the specification says anything about foreach
calling Reset
- and that's a good job, as many implementations don't really implement it (they throw an exception instead).
Basically, you should make your GetEnumerator()
method return a new object which keeps the mutable state of the "cursor" over your data. Note that the simplest way of implementing an iterator in C# is usually to use an iterator block (yield return
etc).
I'd also strongly encourage you to implement the generic interfaces rather than just the non-generic ones; that way your type can be used much more easily in LINQ code, the iterator variable in a foreach
statement can be implicitly typed appropriately, etc.
See more on this question at Stackoverflow