I have following problem:
I want to implement my own collection, which will also implement ICollection<T> interface. That means that I also need to implement IEnumerable<T> interface. Implementing IEnumerable<T> can be done two ways:
First approach: by having private struct implementing IEnumerator<T> and returning it from GetEnumerator() method
Second approach: I can just use iterator (using yield return) and let compiler generate IEnumerator class for me.
I also want my enumerators to throw InvalidOperationException on any MoveNext() (or Reset()) call after the collection was modified
as it is specified in MS documentation
If I will use first approach, then everything is ok (I just save version of my collection when creating new enumerator and then in MoveNext() I just check if it hasnt changed).
The same trick is used by MS Collections But here is THE PROBLEM - if I will use second approach I think I cannot enforce following behaviour. Consider following code.
class MyCollection : ICollcetion<T>
{
int _version = 0;
T[] _collectionData;
public void Modify()
{
... // do the modification
_version++; // change version, so enumerators can check that they are invalid
}
...
public IEnumerator<T> GetEnumerator()
{
int _enmVersion = _version;
int i = 0;
yield return _collectionData[i];
if( _enmVersion != _version ) // check if there was not modificaction
{
throw new InvalidOperationException();
}
}
...
}
...
var collection = new MyCollection();
var e = collection.GetEnumerator();
myCollection.Modfiy(); //collection modification, so e becomes irrecoverably invalidated
e.MoveNext(); // right now I want to throw exception
//but instead of that, code until first yield return executed
//and the version is saved... :(
This problem occurs only if the collection is modified after obataining enumerator object, but before first call to MoveNext(), but still it is problem...
My question is: is it possible somehow use second approach AND enforce right behaviour OR I need for this case to stick with first approach ?
My suspicion is that I have to use first approach, since the code of iterator method is exeuted only when I call MoveNext() , and not in constructor of compiler generated class, but I want to be sure...