是否可以确定IEnumerable <t>是否延迟执行挂起?</t>

时间:2012-03-08 20:56:15

标签: c# linq deferred-execution

我有一个接受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(),不在白名单上的任何内容。

谢谢大家的帮助。

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

4 个答案:

答案 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;
} 
相关问题