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

C# LINQ-IEnumerable.ToList()和延迟执行混淆,c#,linq,deferred-execution,C#,Linq,Deferred Execution,我有一个名为“query”的IEnumerable变量,它表示实体框架查询。如果我执行以下操作,它会多次枚举查询吗?我的困惑在于“result”是一个IEnumerable而不是列表 IEnumerable<T> result = query.ToList(); bool test = result.Any(); IEnumerable result=query.ToList(); bool test=result.Any(); 为此: // Using List to only

我有一个名为“query”的IEnumerable变量,它表示实体框架查询。如果我执行以下操作,它会多次枚举查询吗?我的困惑在于“result”是一个IEnumerable而不是列表

IEnumerable<T> result = query.ToList();
bool test = result.Any();
IEnumerable 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只枚举查询一次
列表结果=query.ToList();
bool test=result.Any();
此外,我知道这很愚蠢,但是如果我执行以下操作,它会枚举两次吗?或者它会不知何故知道“query”已经被枚举,即使枚举的结果没有被使用吗

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

谢谢

否枚举occour仅在ToList()中


一旦调用
ToList
ToArray
您将创建一个内存中的集合。从那时起,你不再处理数据库了

因此,即使您将其声明为
IEnumerable
,它实际上仍然是
query.ToList()之后的
列表

此外,所有相关的LINQ扩展方法都将检查序列是否可以强制转换为集合类型。您可以看到,例如,如果可能,将使用
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;
}
公共静态整数计数(此IEnumerable源){
if(source==null)抛出错误.ArgumentNull(“source”);
ICollection collectionoft=源作为ICollection;
如果(collectionoft!=null)返回collectionoft.Count;
ICollection collection=源作为ICollection;
if(collection!=null)返回collection.Count;
整数计数=0;
使用(IEnumerator e=source.GetEnumerator()){
检查{
而(e.MoveNext())count++;
}
}
返回计数;
}
根据您的上一个问题,如果您是否使用此代码段中的列表或再次使用查询会产生影响:

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

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

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

不管你怎么称呼
List
,或者你把它放在什么类型的变量中,列表本身对
IQueryable
一无所知。多次迭代包含该列表的变量将简单地多次迭代该列表


正如列表对查询一无所知一样,查询对列表一无所知。它不记得它的项目被放入列表并继续返回这些项目。(从理论上讲,您实际上可以这样编写查询对象。它被称为备忘录化,用于查询在迭代时缓存其结果,并在以后迭代时继续从该缓存中提供对象。EF默认情况下不会备忘录化其查询,默认情况下也不会提供备忘录化工具,尽管第三方工具提供了这样的扩展这意味着您拥有的第三个代码段实际上将执行两个独立的数据库查询,而不仅仅是一个。

Tim,OP谈论的是实体框架。而且IEnumerable实际上是一个列表,这让人很困惑。@SriramSakthivel一旦你调用
ToList
你就不再处理EF查询了。蒂姆,我想知道,如果集合是一个列表,讨论计数优化是否实际上只是让事情变得更混乱,而不是更简单。@Servy我知道,我想蒂姆编辑了答案。我的评论是在编辑之前,我非常喜欢编辑;确实使答案更容易理解。
ToList()
很可能会将您的
IEnumerable
转换为
列表
,因此会对其进行迭代。但是
Any()
只检查
IEnumerable
是否包含任何元素,因此它几乎肯定不会枚举您的结果。第三个版本将生成两个db查询,如果
query
IQueryable
它将枚举查询@query.ToList();只有