关于c#:在列表中选择和ForEach<>

Select and ForEach on List<>

本问题已经有最佳答案,请猛点这里访问。

我对C还比较陌生,尝试使用lambda表达式。

我有一个物品清单。我想从列表中选择项目并对所选项目执行foreach操作。我知道我可以不使用lambda表达式来完成它,但我想知道是否可以使用lambda表达式。

所以我想达到一个类似的结果

1
2
3
4
5
6
7
        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";
        }

这是可能的

1
      users.Where(i => i.UserName=="").ToList().ForEach(i=>i.UserName="NA");

但不是这样的

1
     users.select(i => i.UserName=="").ForEach(i=>i.UserName="NA");

有人能解释一下这种行为吗?


从这里开始:好的。

I am having a list of object.

Ok.

很重要的一点是要理解,虽然这句话很准确,但却让C程序员想要更多。什么样的物体?在.NET世界中,始终记住您使用的对象的具体类型是值得的。在这种情况下,该类型是UserProfile。这似乎是一个附带的问题,但它很快就会与具体问题更加相关。你想说的是:好的。

I have a list of UserProfile objects.

Ok.

现在让我们看一下您的两个表达式:好的。

users.Where(i => i.UserName=="").ToList().ForEach(i=>i.UserName="NA");

Ok.

和好的。

users.Where(i => i.UserName=="").ForEach(i=>i.UserName="NA");

Ok.

不同之处(除了只有第一个编译或工作)是需要调用.ToList()Where()函数的结果转换为列表类型。现在,我们开始了解为什么在使用.NET代码时,您总是希望根据类型进行思考,因为现在您应该会想,"那么,我使用的是哪种类型?"我很高兴你问。好的。

.Where()函数产生一个IEnumerable类型,实际上它本身并不是一个完整的类型。它是一个描述某些事情的接口,实现它的契约的类型将能够做到。IEnumerable接口一开始可能会令人困惑,但需要记住的是,它定义了一些可以与foreach循环一起使用的东西。这是唯一的目的。.NET中可以用于foreach循环的任何内容:数组、列表、集合&mdash;它们几乎都实现了IEnumerable接口。还有其他的事情你也可以循环。例如,字符串。现在有许多方法需要将列表或数组作为参数,只要将该参数类型更改为IEnumerable,就可以使其更加强大和灵活。好的。

.NET还可以方便地创建基于状态机的迭代器,用于此接口。这对于创建本身不包含任何项的对象特别有用,但确实知道如何以特定的方式循环在不同集合中的项。例如,我可以在大小为20的数组中只对项目3到12进行循环。或者可以按字母顺序在项目上循环。重要的是,我可以做到这一点,而不需要复制或复制原件。这使得它在内存方面非常高效,它的结构以这样的方式,您可以很容易地组成不同的迭代器在一起,以获得非常强大的结果。好的。

IEnumerable型尤为重要,因为它是构成LINQ系统核心的两种类型之一(另一种是可查询的)。可以使用的大多数.Where().Select().Any()等LINQ运算符都定义为IEnumerable的扩展。好的。

但现在我们有一个例外:ForEach()。此方法不是IEnumerable的一部分。它直接定义为List类型的一部分。因此,我们再次看到,理解您一直在使用的类型非常重要,包括构成完整语句的每个不同表达式的结果。好的。

探究为什么这个特定的方法不是IEnumerable的一部分也是有指导意义的。我相信答案在于LINQ系统从函数式编程世界中获得了很多灵感。在函数式编程中,您希望操作(函数)只做一件事,而不产生副作用。理想情况下,这些函数不会更改原始数据,而是返回新数据。ForEach()方法隐含的所有内容都是关于创建会改变数据的不良副作用。这只是糟糕的功能风格。此外,For()中断方法链接,因为它不返回新的InEdaby。好的。

这里还有另外一节课要学。让我们看一下您的原始代码片段:好的。

1
2
3
4
5
6
7
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项在集合上循环而不复制它的内容吗?想想如果你用这种方式编写代码会发生什么,而不是:好的。

1
2
3
4
5
6
7
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() {...}.Foreach( ... ):前者仍然是完美的风格。好的。好啊。


当然,这很简单。List有一个foreach方法。对于IEnumerable没有这样的方法或扩展方法。

至于为什么一个人有办法而另一个人没有,这是一种观点。如果你对这个话题感兴趣的话,埃里克·利珀特写了一篇博客。