Reputation: 541
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...
Upvotes: 3
Views: 576
Reputation: 887275
Your problem is that an iterator method won't run any code at all until the first MoveNext()
call.
You can work around this problem by wrapping the iterator in a non-iterator method that grabs the version immediately and passes it as a parameter:
public IEnumerator<T> GetEnumerator() {
return GetRealEnumerator(_version);
}
private IEnumerator<T> GetRealEnumerator(int baseVersion) {
Upvotes: 1