对 Enumerable中的所有元素执行特定操作

发布于 2024-07-13 13:32:57 字数 455 浏览 10 评论 0原文

我有一个 Enumerable 并且正在寻找一种允许我为每个元素执行操作的方法,有点像 Select 但有副作用。 比如:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

或者

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

我确实尝试了 Select(s=> { Console.WriteLine(s); return s; }),但它没有打印任何内容。

I have an Enumerable<T> and am looking for a method that allows me to execute an action for each element, kind of like Select but then for side-effects. Something like:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

or

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

I did try Select(s=> { Console.WriteLine(s); return s; }), but it wasn't printing anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

你另情深 2024-07-20 13:32:57

获得此信息的一种快速简便的方法是:

Names.ToList().ForEach(e => ...);

A quick-and-easy way to get this is:

Names.ToList().ForEach(e => ...);
去了角落 2024-07-20 13:32:57

免责声明:这篇文章不再类似于我原来的答案,而是结合了我此后获得的大约七年的经验。 我进行了编辑,因为这是一个备受关注的问题,现有的答案都没有真正涵盖所有角度。 如果您想查看我的原始答案,可以在这篇文章的修订历史记录中找到。


这里首先要了解的是 C# linq 操作,例如 Select()All()Where() 等,都有其根源在函数式编程中。 这个想法是将函数式编程的一些更有用、更容易理解的部分引入 F# 之外的更大的 .Net 世界。 这很重要,因为函数式编程的一个关键原则是操作没有副作用。 很难低估这一点。 但是,在 ForEach()/each() 的情况下,副作用就是操作的全部目的。 添加 each()ForEach() 不仅超出了其他 linq 运算符的函数式编程范围,而且与它们直接相反。

但我知道这让人感到不满意。 它可能有助于解释为什么框架中省略了 ForEach(),但未能解决当前的实际问题。 你有一个真正的问题需要解决。 为什么所有这些象牙塔哲学会妨碍一些可能真正有用的东西呢?

当时 C# 设计团队的 Eric Lippert 可以在这方面为我们提供帮助。 他建议使用传统的 foreach循环:

[ForEach()] 为语言添加了零新的表示能力。 这样做可以让您重写这段完全清晰的代码:

foreach(Foo foo in foos){涉及foo的语句; }

进入这段代码:

foos.ForEach(foo=>{涉及foo的语句; });

他的观点是,当你仔细观察你的语法选项时,你不会获得与传统的 foreach 循环相比,ForEach() 扩展中的任何新内容。 我部分不同意。 想象一下您有这样的情况:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate))
 {
     // now do something
 } 

这段代码混淆了含义,因为它将 foreach 关键字与循环中的操作分开。 它还列出了定义循环操作顺序的操作之前的循环命令。 让这些操作先进行,然后将循环命令放在查询定义的末尾感觉更自然。 另外,代码很丑陋。 似乎能够这样写会好得多:

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

然而,即使在这里,我最终还是同意了埃里克的观点。 我意识到像上面看到的代码正在调用一个附加变量。 如果您有一组像这样的复杂 LINQ 表达式,则可以通过首先将 LINQ 表达式的结果分配给一个新的命名良好变量来向代码中添加有价值的信息:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

此代码感觉更自然。 它将 foreach 关键字放回到循环其余部分旁边,以及查询定义之后。 最重要的是,变量名称可以添加新信息,这将有助于未来程序员尝试理解 LINQ 查询的目的。 我们再次看到所需的 ForEach() 运算符实际上没有为该语言添加新的表达能力。

但是,我们仍然缺少假设的 ForEach() 扩展方法的两个功能:

  1. 它不可组合。 我无法在 foreach 之后添加进一步的 .Where()GroupBy()OrderBy()与其余代码内联循环,无需创建新语句。
  2. 这并不懒惰。 这些操作会立即发生。 例如,它不允许我拥有一种表单,用户可以在其中选择一个操作作为较大屏幕中的一个字段,直到用户按下命令按钮后才会执行该操作。 这种形式可能允许用户在执行命令之前改变主意。 对于 LINQ 查询来说,这是完全正常的(简单),但对于 foreach 来说则不然。

(FWIW,大多数天真的 .ForEach() 实现也存在这些问题。但是可以在没有它们的情况下制作一个。)

当然,您可以制作自己的 ForEach() > 扩展方法。 其他几个答案已经实现了这种方法; 事情并没有那么复杂。 不过,我觉得没有必要。 从语义和操作的角度来看,已经有一种现有的方法适合我们想要做的事情。 上述两个缺失的功能都可以通过使用现有的 Select() 操作来解决。

Select() 适合上面两个示例描述的转换或投影类型。 但请记住,我仍然会避免产生副作用。 对 Select() 的调用应返回新对象或原始对象的投影。 有时可以通过使用匿名类型或动态对象来帮助实现这一点(当且仅当必要时)。 如果您需要将结果保留在原始列表变量中,则可以随时调用 .ToList() 并将其分配回原始变量。 我将在这里补充一点,与更具体的类型相比,我更喜欢尽可能多地使用 IEnumerable 变量。

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

总结:

  1. 大多数时候只需坚持使用 foreach 即可。
  2. foreach 确实 不起作用时(可能不像您想象的那么频繁),请使用 Select()
  3. 当您需要使用Select(),您通常仍然可以通过投影到匿名类型来避免(程序可见的)副作用。
  4. 避免调用 ToList() 的麻烦。 您并不像您想象的那样需要它,而且它可能会对性能和内存使用产生严重的负面影响。

Disclaimer: This post no longer resembles my original answer, but rather incorporates the some seven years experience I've gained since. I made the edit because this is a highly-viewed question and none of the existing answers really covered all the angles. If you want to see my original answer, it's available in the revision history for this post.


The first thing to understand here is C# linq operations like Select(), All(), Where(), etc, have their roots in functional programming. The idea was to bring some of the more useful and approachable parts of functional programming to the larger .Net world beyond F#. This is important, because a key tenet of functional programming is for operations to be free of side effects. It's hard to understate this. However, in the case of ForEach()/each(), side effects are the entire purpose of the operation. Adding each() or ForEach() is not just outside the functional programming scope of the other linq operators, but in direct opposition to them.

But I understand this feels unsatisfying. It may help explain why ForEach() was omitted from the framework, but fails to address the real issue at hand. You have a real problem you need to solve. Why should all this ivory tower philosophy get in the way of something that might be genuinely useful?

Eric Lippert, who was on the C# design team at the time, can help us out here. He recommends using a traditional foreach loop:

[ForEach()] adds zero new representational power to the language. Doing this lets you rewrite this perfectly clear code:

foreach(Foo foo in foos){ statement involving foo; }

into this code:

foos.ForEach(foo=>{ statement involving foo; });

His point is, when you look closely at your syntax options, you don't gain anything new from a ForEach() extension versus a traditional foreach loop. I partially disagree. Imagine you have this:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate))
 {
     // now do something
 } 

This code obfuscates meaning, because it separates the foreach keyword from the operations in the loop. It also lists the loop command prior to the operations that define the sequence on which the loop operates. It feels much more natural to want to have those operations come first, and then have the the loop command at the end of the query definition. Also, the code is just ugly. It seems like it would be much nicer to be able to write this:

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

However, even here, I eventually came around to Eric's point of view. I realized code like you see above is calling out for an additional variable. If you have a complicated set of LINQ expressions like that, you can add valuable information to your code by first assigning the result of the LINQ expression to a new well-named variable:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

This code feels more natural. It puts the foreach keyword back next to the rest of the loop, and after the query definition. Most of all, the variable name can add new information that will be helpful to future programmers trying to understand the purpose of the LINQ query. Again, we see the desired ForEach() operator really added no new expressive power to the language.

However, we are still missing two features of a hypothetical ForEach() extension method:

  1. It's not composable. I can't add a further .Where() or GroupBy() or OrderBy() after a foreach loop inline with the rest of the code, without creating a new statement.
  2. It's not lazy. These operations happen immediately. It doesn't allow me to, say, have a form where a user chooses an operation as one field in a larger screen that is not acted on until the user presses a command button. This form might allow the user to change their mind before executing the command. This is perfectly normal (easy even) with a LINQ query, but not as simple with a foreach.

(FWIW, most naive .ForEach() implementations also have these issues. But it's possible to craft one without them.)

You could, of course, make your own ForEach() extension method. Several other answers have implementations of this method already; it's not all that complicated. However, I feel like it's unnecessary. There's already an existing method that fits what we want to do from both semantic and operational standpoints. Both of the missing features above can be addressed by use of the existing Select() operation.

Select() fits the kind of transformation or projection described by both of the examples above. Keep in mind, though, that I would still avoid creating side effects. The call to Select() should return either new objects or projections from the originals. This can sometimes be aided through the use of an anonymous type or dynamic object (if and only if necessary). If you need the results to persist in, say, an original list variable, you can always call .ToList() and assign it back to your original variable. I'll add here that I prefer working with IEnumerable<T> variables as much as possible over more concrete types.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

In summary:

  1. Just stick with foreach most of the time.
  2. When foreach really won't do (which probably isn't as often as you think), use Select()
  3. When you need to use Select(), you can still generally avoid (program-visible) side effects, possibly by projecting to an anonymous type.
  4. Avoid the crutch of calling ToList(). You don't need it as much as you might think, and it can have significant negative consequence for performance and memory use.
晚风撩人 2024-07-20 13:32:57

您正在寻找目前仅存在于 List 通用集合中的难以捉摸的 ForEach。 网上有很多关于 Microsoft 是否应该将其添加为 LINQ 方法的讨论。 目前,您必须自己动手:

public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}

虽然 All() 方法提供了类似的功能,但它的用例是对每个项目而不是操作执行谓词测试。 当然,可以说服它执行其他任务,但这会在一定程度上改变语义,并使其他人更难解释您的代码(即使用 All() 进行谓词测试或行动?)。

You are looking for the ever-elusive ForEach that currently only exists on the List generic collection. There are many discussions online about whether Microsoft should or should not add this as a LINQ method. Currently, you have to roll your own:

public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}

While the All() method provides similar abilities, it's use-case is for performing a predicate test on every item rather than an action. Of course, it can be persuaded to perform other tasks but this somewhat changes the semantics and would make it harder for others to interpret your code (i.e. is this use of All() for a predicate test or an action?).

谁许谁一生繁华 2024-07-20 13:32:57

不幸的是,在当前版本的 LINQ 中没有内置方法可以执行此操作。 框架团队忽略了添加 .ForEach 扩展方法。 在下面的博客上现在正在进行关于此问题的很好的讨论。

http://blogs.msdn.com/kirillosenkov/archive/2009 /01/31/foreach.aspx

不过添加一个相当容易。

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

Unfortunately there is no built-in way to do this in the current version of LINQ. The framework team neglected to add a .ForEach extension method. There's a good discussion about this going on right now on the following blog.

http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

It's rather easy to add one though.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}
〆凄凉。 2024-07-20 13:32:57

您无法立即使用 LINQ 和 IEnumerable 执行此操作 - 您需要实现自己的扩展方法,或者使用 LINQ 将枚举转换为数组,然后调用 Array.ForEach():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

请注意,由于值类型和结构的方式工作,如果集合是值类型并且您以这种方式修改集合的元素,它将不会对原始集合的元素产生影响。

You cannot do this right away with LINQ and IEnumerable - you need to either implement your own extension method, or cast your enumeration to an array with LINQ and then call Array.ForEach():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

Please note that because of the way value types and structs work, if the collection is of a value type and you modify the elements of the collection this way, it will have no effect on the elements of the original collection.

溺渁∝ 2024-07-20 13:32:57

由于 LINQ 被设计为查询功能而不是更新功能,因此您找不到在 IEnumerable上执行方法的扩展。 因为这将允许您执行一个方法(可能有副作用)。 在这种情况下,你不妨坚持

foreach(名称中的字符串名称)
Console.WriteLine(名称);

Because LINQ is designed to be a query feature and not an update feature you will not find an extension which executes methods on IEnumerable<T> because that would allow you to execute a method (potentially with side effects). In this case you may as well just stick with

foreach(string name in Names)
Console.WriteLine(name);

樱花坊 2024-07-20 13:32:57

使用并行 Linq:

Names.AsParallel().ForAll(name => ...)

Using Parallel Linq:

Names.AsParallel().ForAll(name => ...)

A君 2024-07-20 13:32:57

好吧,您也可以使用标准的 foreach 关键字,只需将其格式化为 oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

抱歉,我认为这个选项应该放在这里:)

Well, you can also use the standard foreach keyword, just format it into a oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

Sorry, thought this option deserves to be here :)

儭儭莪哋寶赑 2024-07-20 13:32:57

List 之外有一个 ForEach 方法。 您可以通过调用 .ToList() 方法将 Enumerable 转换为 List,然后调用 ForEach 方法。

或者,我听说有人从 IEnumerable 定义自己的 ForEach 方法。 这可以通过本质上调用 ForEach 方法来完成,但将其包装在扩展方法中:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}

There is a ForEach method off of List. You could convert the Enumerable to List by calling the .ToList() method, and then call the ForEach method off of that.

Alternatively, I've heard of people defining their own ForEach method off of IEnumerable. This can be accomplished by essentially calling the ForEach method, but instead wrapping it in an extension method:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}
向日葵 2024-07-20 13:32:57

正如之前提到的,ForEach 扩展将进行修复。

我对当前问题的提示是如何执行迭代器

[我确实尝试了 Select(s=> { Console.WriteLine(s); return s; }),但它没有打印任何内容.]

检查这个

_= Names.Select(s=> { Console.WriteLine(s); return 0; }).Count();

尝试一下!

As mentioned before ForEach extension will do the fix.

My tip for the current question is how to execute the iterator

[I did try Select(s=> { Console.WriteLine(s); return s; }), but it wasn't printing anything.]

Check this

_= Names.Select(s=> { Console.WriteLine(s); return 0; }).Count();

Try it!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文