我有一个接受Enumerable的函数。我需要确保对枚举数进行评估,但是如果它在List或其他一些“冻结”集合中已经准备就绪,我宁愿不创建它的副本(例如,通过ToList()或ToArray())。冻结我指的是已经建立了一组项目的集合,例如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(),不在白名单上的任何内容。
谢谢大家的帮助。
编辑* 经过更多的反思,我意识到这相当于停止问题。非常不可能。
答案 0 :(得分:6)
对我有用的东西:
IEnumerable<t> deffered = someArray.Where(somecondition);
if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq"))
{
//this is a deffered executin IEnumerable
}
答案 1 :(得分:5)
你可以尝试对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#集合 - 你需要检查它。
答案 2 :(得分:1)
如果你想确保它“冻结”,我会避免它。 Array元素和List&lt;&gt;可以随时更改(即臭名昭着的“迭代期间更改的集合”例外)。如果你真的需要确保评估IEnumerable而不是在代码下面改变,而不是将所有项目复制到你自己的List / Array中。
可能有其他原因尝试它 - 即运行时内的一些操作会对集合进行特殊检查,以便对其进行优化。或者除了通用IEnumerable之外,还有专门的接口,如ICollection或IQueryable。
编辑:迭代期间集合更改的示例:
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;
}
答案 3 :(得分:0)
如果可以在你的情况下使用包装器,你可以做这样的事情
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;
}