dlf
dlf

Reputation: 9393

How to defer execution of IEnumerable-returning function that begins a LINQ expression?

Suppose I have something like this:

IEnumerable<T> result = BuildAList<T>()
   // lots of LINQ operators
   ;

But maybe BuildAList is expensive, and I don't want to call it unless and until someone actually starts iterating over result. Is there a simple, idiomatic way to do this?

(I can obviously write my own IEnumerable<T> class, but I'm hoping there's a way to do it with the existing operators etc.)

Upvotes: 1

Views: 439

Answers (3)

Ivan Stoev
Ivan Stoev

Reputation: 205749

I'm afraid currently there is no standard method for doing that, so you have to roll your own. It cannot be extension method, so should be regular static method with iterator block (using yield return) and factory delegate similar to Lazy<T> which will ensure the desired deferred execution.

Something like this:

public static class Iterators
{
    public static IEnumerable<T> Lazy<T>(Func<IEnumerable<T>> factory)
    {
        foreach (var item in factory())
            yield return item;
    }
}

And use it as follows:

var result = Iterators.Lazy(() => BuildAList<T>());
// lots of LINQ operators
;

Edit: The downside of the above implementation is that the factory method will be invoked for each execution of the returned enumerable. It can be avoided by combining the implementation with Lazy<T> class:

public static class Iterators
{
    public static IEnumerable<T> Lazy<T>(Func<IEnumerable<T>> factory)
    {
        return LazyIterator(new Lazy<IEnumerable<T>>(factory));
    }

    private static IEnumerable<T> LazyIterator<T>(Lazy<IEnumerable<T>> source)
    {
        foreach (var item in source.Value)
            yield return item;
    }
}

Upvotes: 4

Mike
Mike

Reputation: 435

You could make result into a lazy property and have the downstream stuff reference that.

class MyClass<T>
{
    IEnumerable<T> result; 

    IEnumerable<T> Result
    {
        get { return result ?? (result = BuildAList()); }
    }

    List<T> BuildAList()
    {
        //...
    }
}

Upvotes: 1

Tarrah Arshad
Tarrah Arshad

Reputation: 74

use this model generic method:

public virtual IList<T> GetList(Func<T, bool> where,
         params Expression<Func<T, object>>[] navigationProperties)
    {
        List<T> list;
        using (var context = new DbEntities())
        {
            IQueryable<T> dbQuery = context.Set<T>();

            //Apply eager loading
            foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                dbQuery = dbQuery.Include<T, object>(navigationProperty);

            list = dbQuery
                .AsNoTracking()
                .Where(where)
                .ToList<T>();
        }
        return list;
    }

Upvotes: 1

Related Questions