列表 <> 上的 Select 和 ForEach [重复]
Posted
技术标签:
【中文标题】列表 <> 上的 Select 和 ForEach [重复]【英文标题】:Select and ForEach on List<> [duplicate] 【发布时间】:2013-07-17 23:51:24 【问题描述】:我对 C# 还很陌生,并且正在尝试使用 lambda 表达式。
我有一个对象列表。我想从列表中选择项目并对所选项目执行 foreach 操作。我知道我可以在不使用 lambda 表达式的情况下做到这一点,但如果可以使用 lambda 表达式,我想这样做。
所以我试图达到类似的结果
List<UserProfile> users = new List<UserProfile>();
..load users with list of users
List<UserProfile> selecteditem = users.Where(i => i.UserName=="").ToList();
foreach(UserProfile item in selecteditem)
item.UserName = "NA";
可以做到
users.Where(i => i.UserName=="").ToList().ForEach(i=>i.UserName="NA");
但不是这样的
users.select(i => i.UserName=="").ForEach(i=>i.UserName="NA");
谁能解释一下这种行为..
【问题讨论】:
见this question blogs.msdn.com/b/ericlippert/archive/2009/05/18/… 您最后的代码 sn-p 是否意味着将Where
作为第一个运算符,而不是 select
?
@I4V 感谢您的链接
【参考方案1】:
让我们从这里开始:
我有一个对象列表。
理解这一点很重要,虽然准确,但 C# 程序员想要更多。什么样的对象?在 .Net 世界中,始终牢记您正在使用的特定类型的对象是值得的。在本例中,该类型为UserProfile
。这似乎是一个附带问题,但它会很快变得与特定问题更加相关。你想说的是:
我有一个 UserProfile 对象列表。
现在让我们看看你的两个表达式:
users.Where(i => i.UserName=="").ToList().ForEach(i=>i.UserName="NA");
和
users.Where(i => i.UserName=="").ForEach(i=>i.UserName="NA");
不同之处(除了只有第一次编译或工作)是您需要调用.ToList()
将Where()
函数的结果转换为List 类型。现在我们开始了解为什么在使用 .Net 代码时,您总是希望根据类型来思考,因为您现在应该想知道,“我在使用什么类型,然后?”很高兴你问。
.Where()
函数产生一个IEnumerable<T>
类型,它本身实际上并不是一个完整的类型。这是一个接口,它描述了实现它的契约的类型将能够做的某些事情。 IEnumerable 接口一开始可能会令人困惑,但要记住的重要一点是,它定义了可以与foreach
循环一起使用的东西。这是它的唯一目的。 .Net 中可以与 foreach 循环一起使用的任何东西:数组、列表、集合——它们几乎都实现了 IEnumerable 接口。您还可以循环访问其他内容。以字符串为例。您现在拥有的许多需要 List 或 Array 作为参数的方法可以通过将参数类型更改为 IEnumerable 来变得更加强大和灵活。
.Net 还可以轻松创建将与此接口一起使用的基于状态机的迭代器。这对于创建本身不包含任何项目但知道如何以特定方式循环不同集合中的项目的对象特别有用。例如,我可能只遍历大小为 20 的数组中的项目 3 到 12。或者可能按字母顺序遍历项目。这里重要的是我可以做到这一点而无需复制或复制原件。这使得它在内存方面非常高效,并且它的结构使得您可以轻松地将不同的迭代器组合在一起以获得非常强大的结果。
IEnumerable<T>
类型特别重要,因为它是构成 linq 系统核心的两种类型之一(另一种是 IQueryable)。您可以使用的大多数 .Where()
、.Select()
、.Any()
等 linq 运算符都定义为 IEnumerable 的扩展。
但现在我们遇到了一个例外:ForEach()
。此方法不是 IEnumerable 的一部分。它直接定义为List<T>
类型的一部分。因此,我们再次看到,始终了解您使用的类型非常重要,包括构成完整语句的每个不同表达式的结果。
了解为什么这个特定方法不是直接属于 IEnumerable 的一部分也很有指导意义。我相信答案在于 linq 系统从函数式编程世界中汲取了很多灵感。在函数式编程中,您希望拥有只做一件事且没有副作用的操作(函数)。理想情况下,这些函数不会改变原始数据,而是会返回新数据。 ForEach()
方法隐含地是关于创建改变数据的不良副作用。这只是糟糕的功能风格。此外,ForEach() 会破坏方法链接,因为它不会返回新的 IEnumerable。
这里还有一课要学。我们来看看你原来的sn-p:
List<UserProfile> users = new List<UserProfile>();
// ..load users with list of users
List<UserProfile> selecteditem = users.Where(i => i.UserName=="").ToList();
foreach(UserProfile item in selecteditem)
item.UserName = "NA";
我之前提到过一些可以帮助您显着改进此代码的内容。还记得关于如何让 IEnumerable 项在集合上循环而不复制它的那一点吗?想想如果你用这种方式编写代码会发生什么:
List<UserProfile> users = new List<UserProfile>();
// ..load users with list of users
var selecteditem = users.Where(i => i.UserName=="");
foreach(UserProfile item in selecteditem)
item.UserName = "NA";
我所做的只是删除了对.ToList()
的调用,但一切仍然有效。唯一改变的是我们避免了复制整个列表。这应该会使这段代码更快。在某些情况下,它可以使代码很多更快。需要记住的一点:使用 linq 运算符方法时,通常最好避免调用 .ToArray()
或 .ToList()
,这可能比您想象的要多得多。
至于foreach() ...
vs .Foreach( ... )
:前者仍然是非常合适的风格。
【讨论】:
【参考方案2】:当然,这很简单。 List
有一个 ForEach 方法。 IEnumerable
没有这样的方法或扩展方法。
至于为什么一个人有方法而另一个人没有,这是一种观点。 Eric Lippert blogged on the topic 如果你对他感兴趣的话。
【讨论】:
以上是关于列表 <> 上的 Select 和 ForEach [重复]的主要内容,如果未能解决你的问题,请参考以下文章