在linq中,在Select()之后使用ToList()

5
在这段代码中,是否需要在Select()之后使用ToList()呢?
var names = someStorage.GetItems().Select(x => x.Name).ToList();

取决于你是否需要一个实际的列表,或者只需要一个可枚举类型就足够了。 - Corak
1
@Corak:这取决于更多因素。如果您枚举超过一次,则“选择”将再次运行。这可能不是理想的情况。“ToList()”强制枚举并提供结果。 - Ed S.
@EdS. - 是的。可枚举(enumerable)意味着你只会枚举一次它。如果你枚举多于一次,那么(i)enumerable是不够的。 - Corak
@Corak:理解正在发生的事情非常重要,这才是关键。 - Ed S.
@EdS. - 同意。了解所使用的工具确实有助于提高工作效率。 - Corak
3个回答

6
Enumerable.ToList方法会触发数据的填充,如果您不调用该方法,则不会获取数据,这将保持查询状态。

ToList(IEnumerable)方法强制立即执行查询并返回一个包含查询结果的列表。您可以将此方法附加到查询中以获得查询结果的缓存副本,MSDN


5
这完全取决于您的代码随后要执行什么操作。使用 Select() 定义的查询会立即针对数据存储区运行,而 ToList() 方法则导致这一过程立即执行。如果没有 ToList(),则其执行将被延迟,直到您首次访问 names 变量为止。
另一个方面是,如果您不使用 ToList(),则每次使用 names 变量时都会针对数据存储区运行查询,而不仅仅是一次,就像使用 ToList() 一样。因此,它也严重依赖于您使用 names 变量的频率(如果只在循环中使用一次,则没有区别,否则 ToList() 更加高效)。

1
这取决于您的分配变量,如果您分配给列表,则需要进行转换。
如果您不调用ToList,它将是一个IEnumerable ,它是枚举器,支持对指定类型的集合进行简单迭代。
ToList将源序列转换为列表。请注意以下几点:
- 签名指定List,而不仅仅是IList。当然,它可以返回List的子类,但似乎没有什么意义。 - 它使用立即执行 - 这里没有延迟执行 - 参数(source)不能为null - 它针对source实现ICollection的情况进行了优化 - 它总是创建一个新的独立列表。
最后两点值得更多讨论。首先,针对ICollection的优化未记录在文档中,但它非常有意义:
- List在内部存储其数据数组 - ICollection公开Count属性,因此List可以创建一个大小恰好正确的数组来开始 - ICollection公开CopyTo方法,以便List可以批量将所有元素复制到新创建的数组中。

参考来源


1
好的,但情况比那更为复杂。如果你调用列表,选择将被评估并存储在列表中。如果您连续枚举可枚举对象(即没有 ToList()),则 Select 将运行多次,这可能是您想要的,也可能不是。例如,调用 Count(),然后执行 foreach,您将评估查询 两次。对于某些操作(如 DB 调用)可能不太好。 - Ed S.
@Ed S 这是不是意味着如果我将 someStorage.GetItems().Select(x => x.Name) 存储在缓存中,并对其进行 foreach - Select 会每次都执行? - mtkachenko
@oblomov:是的,它确实会。每次枚举IEnumerable时都会这样。 - Ed S.

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