显式 IEnumerator 实现<T> VS 收益率回报实现



我有以下问题:

我想实现我自己的集合,它也将实现ICollection<T>接口。这意味着我还需要实现IEnumerable<T>接口。可以通过两种方式实现IEnumerable<T>

第一种方法:通过私有结构实现IEnumerator<T>并从GetEnumerator()方法返回它

第二种方法:我可以使用迭代器(使用 yield return ),让编译器为我生成IEnumerator类。

我还希望我的枚举器在修改集合后对任何MoveNext()(或Reset())调用InvalidOperationException正如 MS 文档中指定的

如果我使用第一种方法,那么一切都很好(我只是在创建新的枚举器时保存我的集合版本,然后在MoveNext()中我只是检查它是否没有改变)。MS 集合使用相同的技巧,但问题是 - 如果我使用第二种方法,我认为我无法强制执行以下行为。请考虑以下代码。

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() 时才执行,而不是在编译器生成的类的构造函数中执行,但我想确定......

您的问题是迭代器方法在第一次MoveNext()调用之前根本不会运行任何代码。

您可以通过将迭代器

包装在非迭代器方法中来解决此问题,该方法会立即获取版本并将其作为参数传递:

public IEnumerator<T> GetEnumerator() {
    return GetRealEnumerator(_version);
}
private IEnumerator<T> GetRealEnumerator(int baseVersion) {

最新更新