У меня следующая проблема:
Я хочу реализовать свою собственную коллекцию, которая также будет реализовывать интерфейс ICollection<T>
. Это означает, что мне также нужно реализовать интерфейс IEnumerable<T>
. Реализовать IEnumerable<T>
можно двумя способами:
Первый подход: с помощью частной структуры, реализующей IEnumerator<T>
и возвращающей ее из метода GetEnumerator()
.
Второй подход: я могу просто использовать итератор (используя yield return
) и позволить компилятору сгенерировать для меня класс IEnumerator
.
Я также хочу, чтобы мои счетчики выдавали InvalidOperationException
при любом вызове MoveNext()
(или Reset()
) после изменения коллекции как указано в документации MS
Если я буду использовать первый подход, то все в порядке (я просто сохраняю версию своей коллекции при создании нового счетчика, а затем в MoveNext()
просто проверяю, не изменилась ли она). Тот же прием используется MS Collections. Но вот < strong>ПРОБЛЕМА - если я буду использовать второй подход, я думаю, что не смогу обеспечить следующее поведение. Рассмотрим следующий код.
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... :(
Эта проблема возникает только в том случае, если коллекция модифицируется после получения объекта перечислителя, но до первого вызова MoveNext()
, но все же это проблема...
Мой вопрос: возможно ли как-то использовать второй подход И обеспечить правильное поведение ИЛИ мне нужно в этом случае придерживаться первого подхода?
Я подозреваю, что мне нужно использовать первый подход, поскольку код метода итератора выполняется только тогда, когда я вызываю MoveNext()
, а не в конструкторе сгенерированного компилятором класса, но я хочу быть уверен...