[英]How can I iterate over a collection and change values with LINQ extension methods?
Lets say I have a collection of Messages which has the properties "UserID" (int) and "Unread" (bool). 假设我有一组消息,其中包含“UserID”(int)和“Unread”(bool)属性。
How can I use LINQ extension methods to set Unread = false, for any Message in the collection in whose UserID = 5? 对于UserID = 5的集合中的任何Message,如何使用LINQ扩展方法设置Unread = false?
So, I know I can do something like: 所以,我知道我可以这样做:
messages.Any(m => m.UserID == 5);
But, how do I set the Unread property of each of those with an extension method as well? 但是,如何使用扩展方法设置每个的Unread属性呢?
Note: I know I should not do this in production code. 注意:我知道我不应该在生产代码中这样做。 I'm simply trying to learn some more LINQ-fu.
我只是想学习更多LINQ-fu。
Actually, this is possible using only the built-in LINQ extension methods without ToList
. 实际上,这可以仅使用没有
ToList
的内置LINQ扩展方法。
I believe that this will perform very similarly to a regular for
loop. 我相信这将与常规
for
循环非常相似。 (I haven't checked) (我还没检查过)
Don't you dare do this in real code. 你不敢在实际代码中这样做。
messages.Where(m => m.UserID == 5)
.Aggregate(0, (m, r) => { m.Unread = false; return r + 1; });
As an added bonus, this will return the number of users that it modified. 作为额外的奖励,这将返回其修改的用户数量。
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
然后提交更改。
Standard LINQ extension methods doesn't include side effects aimed methods. 标准LINQ扩展方法不包括针对方法的副作用。 However you can either implement it yourself or use from Reactive Extensions for .NET (Rx) like this:
但是,您可以自己实现它,也可以使用Reactive Extensions for .NET(Rx),如下所示:
messages.Where(m => m.UserID == 5).Run(m => m.Unread = false);
As there is no explicit extension method that does a ForEach
, you are stuck with either using a secondary library, or writing the foreach statement on your own. 由于没有明确的扩展方法可以执行
ForEach
,因此您无论是使用辅助库还是自己编写foreach语句。
foreach (Message msg in messages.Where(m => m.UserID == 5))
{
msg.Unread = false;
}
If you really want to use a Linq statement to accomplish this, create a copy the collection using the ToList()
method, accessing the ForEach()
method of the List
type: 如果您确实想使用Linq语句来完成此操作,请使用
ToList()
方法创建集合的副本,访问List
类型的ForEach()
方法:
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
or place the side-effect in a Where()
statement: 或将副作用放在
Where()
语句中:
messages.Where(m =>
{
if (m.UserID == 5) { m.Unread = false; return true; }
return false;
});
In either case, I prefer to use the explicit foreach
loop as it doesn't make unnecessary copies and is clearer than the Where
hack. 在任何一种情况下,我更喜欢使用显式的
foreach
循环,因为它不会产生不必要的副本,并且比Where
hack更清晰。
With LINQ you can't because LINQ is a query language/extension. 使用LINQ你不能,因为LINQ是一种查询语言/扩展。 There is however a project called MoreLinq , which defines an extension method called ForEach which allows you to pass an action which will be performed on every element.
然而,有一个名为MoreLinq的项目,它定义了一个名为ForEach的扩展方法,它允许您传递将在每个元素上执行的操作。
So, you could do with MoreLinq: 所以,你可以使用MoreLinq:
messages.Where(m => m.UserID == 5).ForEach(m => m.Unread = false);
Best Regards, 最好的祝福,
Oliver Hanappi 奥利弗哈纳皮
This answer is in the spirit of providing a solution. 这个答案是本着提供解决方案的精神 。 On could create an extension which does both the predicate (
Where
extension) to weed out the items and the action needed upon those items. On可以创建一个扩展,它既可以执行谓词(
Where
扩展),也可以执行这些项目所需的操作。
Below is an extension named OperateOn
which is quite easy to write: 下面是一个名为
OperateOn
的扩展,它很容易编写:
public static void OperateOn<TSource>(this List<TSource> items,
Func<TSource, bool> predicate,
Action<TSource> operation)
{
if ((items != null) && (items.Any()))
{
items.All (itm =>
{
if (predicate(itm))
operation(itm);
return true;
});
}
}
Here is it in action: 这是它的实际应用:
var myList = new List<Item>
{ new Item() { UserId = 5, Name = "Alpha" },
new Item() { UserId = 5, Name = "Beta", UnRead = true },
new Item() { UserId = 6, Name = "Gamma", UnRead = false }
};
myList.OperateOn(itm => itm.UserId == 5, itm => itm.UnRead = true);
Console.WriteLine (string.Join(" ",
myList.Select (itm => string.Format("({0} : {1})",
itm.Name,
itm.UnRead ))));
/* Outputs this to the screen
(Alpha : True) (Beta : True) (Gamma : False)
*/
... ...
public class Item
{
public bool UnRead { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
}
You should be able to just do it in a Select(), remember the lambda is a shortcut for a function, so you can put as much logic in there as you want, then return the current item being enumerated. 您应该能够在Select()中执行此操作,记住lambda是函数的快捷方式,因此您可以根据需要放置尽可能多的逻辑,然后返回枚举的当前项。 And... why exactly wouldn't you do this in production code?
并且......为什么你不能在生产代码中这样做?
messages = messages
.Select(m =>
{
if (m.UserId == 5)
m.Unread = true;
return m;
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.