给定示例类:
internal Stuff<T> : IEnumerable<T> where T : Foo
{
private readonly object Sync = new object();
private readonly T[] TObjects;
public IEnumerator<T> GetEnumerator()
{
lock(Sync)
using (IEnumerator<T> safeEnum = TObjects.AsEnumerable().GetEnumerator())
while (safeEnum.MoveNext())
yield return safeEnum.Current;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
//other things
}
如果我在 foreach 循环中迭代这个类,则整个 foreach 循环被对象的锁定方案锁定。 我的问题是,如果我在 linq 查询中使用它会发生什么,例如:
Stuff stuff = new Stuff() { /*some foos*/ };
var things = stuff.Where(/*some predicate*/);
foreach(Foo foo in things)
{
//am i locked?
}
foreach(Foo foo in stuff.Where(/*some predicate*/))
{
//am i locked?
}
所以它归结为,查询链接如何在引擎盖下工作?
让我们详细回顾一下你的代码,除了stuff<T>
应该是一个类的小错别字之外,你对IEnumerator<T>
的实现如下:
public IEnumerator<T> GetEnumerator()
{
lock (Sync)
using (IEnumerator<T> safeEnum = TObjects.AsEnumerable().GetEnumerator())
while (safeEnum.MoveNext())
yield return safeEnum.Current;
}
这是Enumeration
的实现,而不仅仅是Enumerator
,因为获取枚举器后,您将通过进一步移动枚举器来进一步处理集合,理想情况下,您的自定义实现应该是这样的:
IEnumerator IEnumerable.GetEnumerator() => TObjects.AsEnumerable().GetEnumerator()
,由于枚举是由foreach
循环完成的,它通过访问实现的枚举器并调用MoveNext
方法和Current
属性来完成枚举,因此当前的实现可以更简洁
枚举器实现
TObjects.AsEnumerable().GetEnumerator()
实现如下,它是访问静态类System.Linq.Enumerable
的GetEnumerator()
实现,该实现内部具有抽象类Iterator<T>
的实现。
AsEnumerable()
是一个简单的扩展方法public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source) { return source; }
现在由于我们的源是一个数组
T[]
,我们正在应用Where
子句,因此如以下枚举实现中所述public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate); return new WhereEnumerableIterator<TSource>(source, predicate); }
我们得到WhereArrayIterator
,其枚举/迭代的实现如下:
class WhereArrayIterator<TSource> : Iterator<TSource>
{
TSource[] source;
Func<TSource, bool> predicate;
int index;
public WhereArrayIterator(TSource[] source, Func<TSource, bool> predicate) {
this.source = source;
this.predicate = predicate;
}
public override Iterator<TSource> Clone() {
return new WhereArrayIterator<TSource>(source, predicate);
}
public override bool MoveNext() {
if (state == 1) {
while (index < source.Length) {
TSource item = source[index];
index++;
if (predicate(item)) {
current = item;
return true;
}
}
Dispose();
}
return false;
}
public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) {
return new WhereSelectArrayIterator<TSource, TResult>(source, predicate, selector);
}
public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
return new WhereArrayIterator<TSource>(source, CombinePredicates(this.predicate, predicate));
}
}
如注释中所述,将枚举器获取到集合是线程安全的,因为它是只读的。所有提到的代码和类相关详细信息都可以在MS参考源上找到,该参考源具有API搜索浏览器