11

I have a function that accepts an Enumerable. I need to ensure that the enumerator is evaluated, but I'd rather not create a copy of it (e.g. via ToList() or ToArray()) if it is all ready in a List or some other "frozen" collection. By Frozen I mean collections where the set of items is already established e.g. List, Array, FsharpSet, Collection etc, as opposed to linq stuff like Select() and where().

Is it possible to create a function "ForceEvaluation" that can determine if the enumerable has deffered execution pending, and then evaluate the enumerable?

 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()}

 }

}

After some more research I've realized that this is pretty much impossible in any practical sense, and would require complex code inspection of each iterator.

So I'm going to go with a variant of Mark's answer and create a white-list of known safe types and just call ToList() anything not on that is not on the white-list.

Thank you all for your help.

Edit* After even more reflection, I've realized that this is equivalent to the halting problem. So very impossible.

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

orに対して有望なチェックを試すこともできますが、これら引き続き遅延実装できることに注意してください-しかし、それははるかにまれであり、LINQ はそれを行いません-イテレータを使用するだけです (遅延コレクションではありません)。そう:IList<T>ICollection<T>

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 に答える