Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to determine if an IEnumerable<T> has deffered execution pending?

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.

like image 362
Jason Hernandez Avatar asked Mar 08 '12 20:03

Jason Hernandez


3 Answers

Something that worked for me way :

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

if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq"))
{
  //this is a deffered executin IEnumerable
}
like image 113
Hanan M Avatar answered Nov 02 '22 23:11

Hanan M


You could try a hopeful check against IList<T> or ICollection<T>, but note that these can still be implemented lazily - but it is much rarer, and LINQ doesn't do that - it just uses iterators (not lazy collections). So:

var list = foos as IList<Foo>;
if(list != null) return list; // unchanged
return foos.ToList();

Note that this is different to the regular .ToList(), which gives you back a different list each time, to ensure nothing unexpected happens.

Most concrete collection types (including T[] and List<T>) satisfy IList<T>. I'm not familiar with the F# collections - you'd need to check that.

like image 43
Marc Gravell Avatar answered Nov 02 '22 23:11

Marc Gravell


I would avoid it if you want to make sure it is "frozen". Both Array elements and List<> can be changed at any time (i.e. infamous "collection changed during iteration" exception). If you really need to make sure IEnumerable is evaluated AND not changing underneath your code than copy all items into your own List/Array.

There could be other reasons to try it - i.e. some operations inside run time do special checks for collection being an array to optimize them. Or have special version for specialized interface like ICollection or IQueryable in addition to generic IEnumerable.

EDIT: Example of collection changing during iteration:

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;
}
like image 1
Alexei Levenkov Avatar answered Nov 03 '22 00:11

Alexei Levenkov