是否有可能确定IEnumerable 是否已将执行挂起?

我有一个接受Enumerable的函数。 我需要确保对枚举数进行评估,但是我不想创建它的副本(例如,通过ToList()或ToArray())如果它已在List或其他“冻结”集合中准备就绪。 冻结我指的是已经建立了一组项目的集合,例如List,Array,FsharpSet,Collection等,而不是像Select()和where()这样的linq。

是否有可能创建一个函数“ForceEvaluation”,可以确定枚举是否已将执行挂起,然后评估可枚举?

public void Process(IEnumerable foos) { IEnumerable evalutedFoos = ForceEvaluation(foos) EnterLockedMode(); // all the deferred processing needs to have been done before this line. foreach (Foo foo in foos) { Bar(foo); } } public IEnumerable ForceEvaluation(IEnumerable foos) { if(??????) { return foos} else {return foos.ToList()} } 

}

经过一些研究后,我意识到这在任何实际意义上都是不可能的,并且需要对每个迭代器进行复杂的代码检查。

因此,我将使用Mark的答案的变体并创建已知安全类型的白名单,并且只需调用ToList()不在白名单上的任何内容。

感谢大家的帮助。

编辑*经过更多的反思,我意识到这相当于停止问题。 所以非常不可能。

可以尝试对IListICollection进行有希望的检查,但请注意,这些仍然可以延迟实现 – 但它更少见,LINQ不会这样做 – 它只使用迭代器(而不是惰性集合) 。 所以:

 var list = foos as IList; if(list != null) return list; // unchanged return foos.ToList(); 

请注意,这与常规.ToList() ,后者每次都会返回不同的列表,以确保不会发生任何意外情况。

大多数具体的集合类型(包括T[]List )满足IList 。 我不熟悉F#集合 – 你需要检查它。

对我有用的东西:

 IEnumerable deffered = someArray.Where(somecondition); if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq")) { //this is a deffered executin IEnumerable } 

如果你想确保它“冻结”,我会避免它。 Array元素和List <>都可以随时更改(即臭名昭着的“迭代期间更改的集合”异常)。 如果你真的需要确保IEnumerable被评估并且不在代码下面改变,那么将所有项目复制到你自己的List / Array中。

尝试它可能有其他原因 – 即运行时内的一些操作会对集合进行特殊检查,以便对其进行优化。 或者除了通用的IEnumerable之外,还有专门的接口,如ICollection或IQueryable。

编辑:迭代期间集合更改的示例:

 IEnumerable collectionAsEnumrable = collection; foreach(var i in collectionAsEnumrable) { // something like following can be indirectly called by // synchronous method on the same thread collection.Add(i.Clone()); collection[3] = 33; } 

如果可以在你的情况下使用包装器,你可以做这样的事情

 public class ForceableEnumerable : IEnumerable { IEnumerable _enumerable; IEnumerator _enumerator; public ForceableEnumerable(IEnumerable enumerable) { _enumerable = enumerable; } public void ForceEvaluation() { if (_enumerator != null) { while (_enumerator.MoveNext()) { } } } #region IEnumerable Members public IEnumerator GetEnumerator() { _enumerator = _enumerable.GetEnumerator(); return _enumerator; } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } 

或者,如果要在任何情况下进行评估,请实现类似的强制方法

 public void ForceEvaluation() { if (_enumerator == null) { _enumerator = _enumerable.GetEnumerator(); } while (_enumerator.MoveNext()) { } } 

编辑:

如果要确保在任何情况下仅对枚举进行一次计算,则可以将GetEnumerator更改为

 public IEnumerator GetEnumerator() { if (_enumerator == null) } _enumerator = _enumerable.GetEnumerator(); } return _enumerator; }