11

我有一个接受 Enumerable 的函数。我需要确保对枚举数进行评估,但如果它在 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() 任何不在白名单上的东西。

感谢大家的帮助。

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

4

4 回答 4

6

对我有用的东西:

IEnumerable<t> deffered = someArray.Where(somecondition);

if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq"))
{
  //this is a deffered executin IEnumerable
}
于 2012-11-19T12:01:37.017 回答
5

可以尝试对IList<T>or进行有希望的检查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# 集合 - 你需要检查一下。

于 2012-03-08T21:00:29.020 回答
1

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

尝试它可能还有其他原因 - 即运行时内的某些操作对集合进行特殊检查以优化它们。或者除了通用 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;
}
于 2012-03-08T21:10:23.837 回答
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
}

或者如果您想在任何情况下进行评估,请执行这样的 force 方法

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;
} 
于 2012-03-08T21:27:54.723 回答