LINQ - IEnumerable.ToList()和延迟执行混淆

时间:2014-08-27 14:00:24

标签: c# linq deferred-execution

我有一个名为“query”的IEnumerable变量,它代表一个实体框架查询。如果我执行以下操作,它会不止一次枚举查询?我的困惑是“结果”是IEnumerable而不是List。

IEnumerable<T> result = query.ToList();
bool test = result.Any();

与此相关:

// Using List to only enumerate the query once
List<T> result = query.ToList();
bool test = result.Any();

另外,我知道这很愚蠢,但如果我执行以下操作,它会枚举两次,还是会以某种方式知道“查询”已经枚举,即使枚举的结果没有被使用?

List<T> result = query.ToList();
bool test = query.Any();

谢谢!

3 个答案:

答案 0 :(得分:3)

在您调用ToListToArray后,您将创建内存中的集合。从那时起,你就不再处理数据库了。

因此,即使您将其声明为IEnumerable<T>,它在List<T>之后仍然是query.ToList()

此外,所有相关的LINQ扩展方法都将检查序列是否可以转换为集合类型。您可以看到,例如在Enumerable.Count中,如果可能,将使用Count属性:

public static int Count<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count;
    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        checked {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}

根据您的上一个问题,如果它有所不同,您可以在此代码段中使用列表或再次查询:

List<T> result = query.ToList();
bool test = query.Any();

是的,在这种情况下,您没有在内存中使用列表,但查询将再次询问数据库,即使.Any不像.ToList那样昂贵。

答案 1 :(得分:1)

当您在查询中调用ToList时,它将转换为列表。查询将在然后进行评估,项目将被拉出,列表将被填充。从那时起,List就不知道原始查询。对该列表的任何操作都不会以任何方式影响或评估该查询,因为它对此一无所知。

你所谓的List或者你坚持使用什么类型的变量并不重要,列表本身对IQueryable的任何信息都不了解。迭代多次保存列表的变量将简单地多次迭代该列表。

就像列表对查询一样不了解的方式一样,查询也不知道列表的内容。它不记得它的项目被放入一个列表并继续返回这些项目。 (实际上,您可以在理论上编写这样的查询对象。它称为memoization,用于查询在迭代时缓存它的结果,并在以后迭代时继续提供来自该缓存的对象.EF不会&#39 ; t默认情况下会记住它的查询,默认情况下它也没有提供记忆的工具,虽然第三方工具提供了这样的扩展。)这意味着你拥有的第三个代码片段实际上将执行两个独立的数据库查询,而不仅仅是一个。

答案 2 :(得分:0)