多次调用ToList方法会影响性能吗?

3

我经常看到调用ToList会导致查询每次执行。对此我有些困惑。在这种情况下,第二次调用ToList会使查询执行超过一次吗?在什么情况下查询会执行多次?

//This gets from database and ToList is already called here
List<Customer> Customers = GetCustomersFromDataBase();

//ToList is called here again
List<Customer> FilteredCustomers = (from c in Customerswhere c.id == 1c).ToList();

编辑

我提供了这个代码示例,以便更好地理解ToList的作用。但我并不是按照这种方式实现它。 此外,我调用了一个存储过程来从数据库中返回客户信息,并从返回的数据表创建了一个客户列表。


你为什么想要这样做?不如在调用ToList()之后存储列表并使用它? - Saransh Kataria
请检查我的编辑。 - user3587180
你是在List()上再次对列表进行操作还是在IQueryable上进行? - Saransh Kataria
如果在代码中多次引用LINQ查询(IQueryable),而不执行.ToList(),则可能会对性能产生潜在和显著的影响。这取决于LINQ提供程序。因此,应该使用LINQ查询而不是集合(IEnumerable)。 - user585968
3个回答

6

在一个 IQueryable 上调用 ToList(或 ToArrayAsEnumerable 等)将会执行查询。之后,您使用的任何 LINQ 表达式都将在内存列表上操作。

例如,您的代码:

List<Customer> Customers = GetCustomersFromDataBase();

这将导致一个ListCustomer对象,它代表数据库记录。该列表现在与用于从数据库中获取结果的查询无关,尽管Customer对象中的链接记录仍需要从数据库中获取。
然后当您执行以下操作时:
List<Customer> FilteredCustomers = (from c in Customers where c.id == 1c).ToList();

这不是在数据库上操作,而是在从前一个语句返回的内存中的结果上进行操作。在这种情况下,您不会多次查询数据库。
影响性能的地方实际上是当您有一个IQueryable对象,您直接多次调用ToList时。每次在IQueryable上调用ToList都将导致对数据库的调用,同时在后台进行全部对象跟踪等操作。通常情况下,这是需要避免的,尽管在枚举结果之前对查询进行附加过滤可能会导致更好的性能,如果查询所选记录的总数很大,并且过滤器拉取数据的子集很小。

1
你的回答似乎很有道理!但我想再确认一件事 - 如果我有一个返回客户IQueryable的WCF服务,那么每次调用ToList时都会调用该服务吗? - user3587180
1
换句话说,如果调用的目标是 IQueryable,那么它将查询它们(数据库|WCF服务|任何其他)。如果它是一个本地集合,那么就不会对(数据库/WCF等)进行查询。 - Corey
1
你对 AsEnumerable 的理解是错误的;它不会执行查询,但是当查询被执行时,在 AsEnumerable 调用之后的部分将由 LINQ to Objects 执行,而不是查询提供程序执行其余部分。 - Jon Hanna
有没有一种返回 ToListIEnumerable<T> 类型的方法,然后再用 ToList 进行转换? 例如: GetCustomers().ToList() - 实际上是: .ToList().ToList() 或者甚至是: .ToList().ToList().ToList().ToList() - Luke T O'Brien
在一个已经是List的对象上调用ToList方法将简单地返回该对象,因此不会有太多开销。只需要一些循环来进行类型检查。 - Corey
显示剩余3条评论

4

在您的情况下,查询仅被执行一次。

这里:

List<Customer> FilteredCustomers = (from c in Customers where c.id == 1c).ToList();

这行代码仅根据变量Customers进行过滤,该变量是通过ToList()填充的客户表的副本。


1
虽然这是一个很好的回答,但它并没有解决“在这种情况下,第二次调用ToList会使查询执行多次吗?在什么情况下查询会执行多次?”这个问题。 - user585968

3

查看Enumerable.ToList()的源代码(在Google上搜索) Enumerable.ToList()

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw Error.ArgumentNull("source");
        return new List<TSource>(source);
}

所以ToList().ToList()是一种浪费,会创建一个新的List,将第一个ToList中所有元素(引用)复制到第二个列表中,导致分配新的内存并且第一个列表被垃圾回收,导致内存被释放。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接