是否可以确定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>;已解除了挂起的执行

对我有用的东西:

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;
}