在列表中选择和ForEach<>

时间:2013-07-18 20:40:22

标签: c# lambda

我对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");

有人可以解释这种行为吗。

2 个答案:

答案 0 :(得分:11)

让我们从这里开始:

  

我有一个对象列表。

重要的是要理解,虽然准确,但这句话让c#程序员想要更多。什么样的对象?在.Net世界中,始终牢记您正在使用的特定类型的对象是值得的。在这种情况下,该类型为UserProfile。这似乎是一个侧面问题,但它将很快与特定问题更加相关。你要说的是:

  

我有一个UserProfile对象列表。

现在让我们看看你的两个表达式:

  

users.Where(i =&gt; i.UserName ==&#34;&#34;)。ToList()。ForEach(i =&gt; i.UserName =&#34; NA&#34;);

  

users.Where(i =&gt; i.UserName ==&#34;&#34;)。ForEach(i =&gt; i.UserName =&#34; NA&#34;);

差异(除了第一次编译或工作之外)是需要调用.ToList()Where()函数的结果转换为List类型。现在我们开始看到为什么在使用.Net代码时,你总是想要在类型方面进行思考,因为现在应该让你想知道,&#34;我是什么类型的与之合作,然后?&#34;我很高兴你问。

.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。

这里还有一个要吸取的教训。我们来看看您的原始代码段:

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( ... ):前者仍然是完全合适的风格。

答案 1 :(得分:5)

当然,这很简单。 ListForEach方法。 IEnumerable没有此类方法或扩展方法。

至于为什么一个人有方法而另一个人没有方法,这是一个意见。 Eric Lippert blogged on the topic如果您对他感兴趣的话。