是否可以确定IEnumerable<;T>;已解除了挂起的执行
本文关键字:挂起 执行 gt IEnumerable 是否 lt | 更新日期: 2023-09-27 18:27:07
我有一个接受枚举的函数。我需要确保枚举器得到评估,但如果它在List或其他"冻结"集合中都准备好了,我宁愿不创建它的副本(例如通过ToList()或ToArray())。我所说的Frozen指的是已经建立项目集的集合,例如List、Array、FsharpSet、Collection等,而不是像Select()和where()这样的linq东西。
是否可以创建一个函数"ForceEvaluation"来确定可枚举对象是否已解除执行挂起,然后评估可枚举对象?
public void Process(IEnumerable<Foo> foos)
{
IEnumerable<Foo> 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<Foo> foos)
{
if(??????)
{ return foos}
else
{return foos.ToList()}
}
}
经过更多的研究,我意识到这在任何实际意义上都是不可能的,并且需要对每个迭代器进行复杂的代码检查。
因此,我将使用Mark答案的变体,创建一个已知安全类型的白名单,只要调用ToList()任何不在白名单上的东西。
谢谢大家的帮助。
编辑*经过更多的思考,我意识到这相当于停顿的问题。太不可能了。
对我有用的东西:
IEnumerable<t> deffered = someArray.Where(somecondition);
if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq"))
{
//this is a deffered executin IEnumerable
}
您可以尝试对IList<T>
或ICollection<T>
进行有希望的检查,但请注意,这些仍然可以延迟实现,但这种情况要少见得多,而且LINQ不这样做,它只使用迭代器(而不是延迟集合)。因此:
var list = foos as IList<Foo>;
if(list != null) return list; // unchanged
return foos.ToList();
请注意,这与常规的.ToList()
不同,后者每次都会返回不同的列表,以确保不会发生意外。
大多数混凝土收集类型(包括T[]
和List<T>
)满足IList<T>
。我不熟悉F#系列——你需要检查一下。
如果你想确保它被"冻结",我会避免它。Array元素和List<>可以随时更改(即臭名昭著的"迭代期间更改集合"异常)。如果你真的需要确保IEnumerable在你的代码下面被评估并且没有改变,那么就把所有项目复制到你自己的List/Array中。
尝试它可能还有其他原因,即运行时内的一些操作会对集合是否为数组进行特殊检查,以优化它们。或者除了通用IEnumerable之外,还有专门接口的特殊版本,如ICollection或IQueryable。
EDIT:迭代期间集合更改示例:
IEnumerable<T> 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<T> : IEnumerable<T>
{
IEnumerable<T> _enumerable;
IEnumerator<T> _enumerator;
public ForceableEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
}
public void ForceEvaluation()
{
if (_enumerator != null) {
while (_enumerator.MoveNext()) {
}
}
}
#region IEnumerable<T> Members
public IEnumerator<T> 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<T> GetEnumerator()
{
if (_enumerator == null) }
_enumerator = _enumerable.GetEnumerator();
}
return _enumerator;
}