显式 IEnumerator<T> 实现 VS yield return 实现
Explicit IEnumerator<T> implementation VS yield return implementation
我有以下问题:
我想实现我自己的集合,它也将实现 ICollection<T>
接口。这意味着我还需要实现 IEnumerable<T>
接口。可以通过两种方式实现 IEnumerable<T>
:
第一种方法: 通过私有结构实现 IEnumerator<T>
并从 GetEnumerator()
方法返回它
第二种方法: 我可以只使用迭代器(使用 yield return
)并让编译器为我生成 IEnumerator
class。
我还希望我的枚举器在修改集合后对任何 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()
时执行,而不是在编译器的构造函数中执行生成了 class,但我想确定...
您的问题是迭代器方法在第一个 MoveNext()
调用之前不会 运行 任何代码。
您可以通过将迭代器包装在非迭代器方法中来解决此问题,该方法立即获取版本并将其作为参数传递:
public IEnumerator<T> GetEnumerator() {
return GetRealEnumerator(_version);
}
private IEnumerator<T> GetRealEnumerator(int baseVersion) {
我有以下问题:
我想实现我自己的集合,它也将实现 ICollection<T>
接口。这意味着我还需要实现 IEnumerable<T>
接口。可以通过两种方式实现 IEnumerable<T>
:
第一种方法: 通过私有结构实现 IEnumerator<T>
并从 GetEnumerator()
方法返回它
第二种方法: 我可以只使用迭代器(使用 yield return
)并让编译器为我生成 IEnumerator
class。
我还希望我的枚举器在修改集合后对任何 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()
时执行,而不是在编译器的构造函数中执行生成了 class,但我想确定...
您的问题是迭代器方法在第一个 MoveNext()
调用之前不会 运行 任何代码。
您可以通过将迭代器包装在非迭代器方法中来解决此问题,该方法立即获取版本并将其作为参数传递:
public IEnumerator<T> GetEnumerator() {
return GetRealEnumerator(_version);
}
private IEnumerator<T> GetRealEnumerator(int baseVersion) {