在列表<>上选择和ForEach

amm*_*mmu 0 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";
        }
Run Code Online (Sandbox Code Playgroud)

有可能做到

      users.Where(i => i.UserName=="").ToList().ForEach(i=>i.UserName="NA");
Run Code Online (Sandbox Code Playgroud)

但不是这样的

     users.select(i => i.UserName=="").ForEach(i=>i.UserName="NA");
Run Code Online (Sandbox Code Playgroud)

有人可以解释这种行为..

Joe*_*orn 11

我们从这里开始:

我有一个对象列表.

重要的是要理解,虽然准确,但该声明让ac#程序员想要更多.什么样的对象?在.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>类型是特别重要的,因为它是两种类型的(另一个是IQueryable的),其形成所述LINQ系统的核心中的一个.在大多数情况下.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";
}
Run Code Online (Sandbox Code Playgroud)

我之前提到过一些可以帮助您显着改进此代码的内容.请记住,关于如何让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";
}
Run Code Online (Sandbox Code Playgroud)

我所做的只是删除了电话.ToList(),但一切仍然有效.唯一改变的是我们避免需要复制整个列表.这应该使这段代码更快.在某些情况下,它可以使代码中的许多更快.需要记住的一点是:使用linq操作符方法时,通常可以避免调用.ToArray().ToList()尽可能避免调用,并且可能比您想象的要多得多.

至于foreach() {...}vs .Foreach( ... ):前者仍然是完全合适的风格.


Ser*_*rvy 5

当然,这很简单. List有一个ForEach方法.没有这样的方法或扩展方法IEnumerable.

至于为什么一个人有方法而另一个没有方法,这是一个意见.如果你对他感兴趣的话,Eric Lippert 就这个话题发表了博客.