【问题标题】:Explicit IEnumerator<T> implementation VS yield return implementation显式 IEnumerator<T> 实现 VS yield return 实现
【发布时间】:2016-06-21 17:27:59
【问题描述】:

我有以下问题:

我想实现我自己的集合,它也将实现ICollection&lt;T&gt; 接口。这意味着我还需要实现IEnumerable&lt;T&gt; 接口。实现IEnumerable&lt;T&gt; 可以通过两种方式完成:

第一种方法:让私有结构实现 IEnumerator&lt;T&gt; 并从 GetEnumerator() 方法返回它

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

我还希望我的枚举器在集合被修改后在任何MoveNext()(或Reset())调用中抛出InvalidOperationException as it is specified in MS documentation

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

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

【问题讨论】:

    标签: c# .net ienumerable yield-return ienumerator


    【解决方案1】:

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

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

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

    【讨论】:

    • 非常感谢,它确实有效。我认为迭代器方法签名需要 GetEnumerator() 才能被编译器注意到。
    • 否;任何方法都可以是迭代器。迭代器也可以返回IEnumerable&lt;T&gt;
    猜你喜欢
    • 2023-03-21
    • 2018-02-15
    • 1970-01-01
    • 2020-06-23
    • 2018-10-22
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多