如何推迟启动LINQ表达式的IEnumerable-returning函数的执行?

时间:2017-04-04 18:13:53

标签: c# linq

假设我有这样的事情:

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

但也许BuildAList代价很高,而且除非有人真正开始迭代result,否则我不想打电话给它。有没有一种简单的,惯用的方法来做到这一点?

(我显然可以编写自己的IEnumerable<T>课程,但我希望有一种方法可以与现有的操作员一起完成。)

3 个答案:

答案 0 :(得分:4)

我担心目前没有标准方法可以做到这一点,所以你必须自己动手。它不能是扩展方法,所以应该是带有迭代器块的常规静态方法(使用yield return)和类似于Lazy<T>的工厂委托,这将确保所需的延迟执行。

这样的事情:

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

使用如下:

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

编辑:上述实现的缺点是,每次执行返回的枚举时都会调用工厂方法。通过将实现与Lazy<T>类:

组合,可以避免这种情况
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;
    }
}

答案 1 :(得分:1)

使用此模型通用方法:

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;
    }

答案 2 :(得分:1)

你可以将结果变成一个惰性属性并让下游的东西引用它。

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

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

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