JohnC
JohnC

Reputation: 367

IEnumerable<T> result from a method and deferred execution

I understand deferred execution in LINQ as explained here: What are the benefits of a Deferred Execution in LINQ?

However, deferred execution can lead to bugs especially in multithreaded scenarios eg. evaluation performed on a shared collection outside of a lock. When a method that returns a deferred evaluation is nested several method layers deep, it can get really hard (at least for me) to remember or keep track of such that I lock appropriately.

Omitting special cases like infinite sequences (eg. Fibonacci) and also assuming the filtering of the collection is deemed complete (ie. it is not likely that the consumer would filter the results further), what would be considered the "best approach" when returning a collection of IEnumerable from a method -- should it already be evaluated or deferred?

Note: "best approach" can be defined in terms of efficiency/code safety of some other measure, just verify in your response. I would like to know how the community does this.

Follow-on question: does it make sense to explicitly state in the method name if the result is evaluated or deferred?

Upvotes: 3

Views: 154

Answers (1)

Daniel A.A. Pelsmaeker
Daniel A.A. Pelsmaeker

Reputation: 50366

Most of the code you'll write is not multi-threaded. In fact, there are only three reasons I can think of when you want to eagerly evaluate an enumerable:

  1. You need it in a multi-threaded environment. You should make it a list or array instead.
  2. You want random access to the enumerable. You should make it a list or array instead.
  3. You want to control when the evaluation takes place (for example if it is expensive).

At other times you should just let it use deferred execution. This postpones the evaluation to the point it is actually needed, and it might be faster depending on the filters you apply. For example, bigquery.First() might be faster than bigquery.ToArray().First(). Can you ever be sure that the user is done filtering?

Also, the runtime will optimize certain LINQ queries. This example is taken from Jon Skeet's article LINQ To Objects and the performance of nested "Where" calls:

// Normal LINQ query
var query = list.Where(x => Condition1(x))
                .Where(x => Condition2(x))
                .Select(x => Projection1(x))
                .Select(y => Projection2(y));

// After optimization
var query = list.WhereSelect(x => Condition1(x) && Condition2(x),
                             x => Projection2(Projection1(x)); 

By the way, your methods should return the most specific visible type they can. For example, a method dealing with T[] arrays or List<T> lists internally should generally not return only an IEnumerable<T>. If you want the result to be immutable, wrap it in a ReadOnlyCollection<T> instead.

Upvotes: 2

Related Questions