44

可能重复:
LINQ 等效于 IEnumerable<T> 的 foreach

List<T>有一个方法被调用ForEach,它在它的每个元素上执行传递的动作。

var names = new List<String>{ "Bruce", "Alfred", "Tim", "Richard" };

names.ForEach(p =>  { Console.WriteLine(p); });

但是如果names不是 aList<T>而是 aIList<T>怎么办?IList<T>没有像ForEach.

有其他选择吗?

4

4 回答 4

48

使用foreach循环:

foreach (var p in names) {
    Console.WriteLine(p);
}

如果实际上并不能提高可读性,就没有理由到处使用委托和扩展方法;循环foreach并不比ForEach方法更明确地告诉读者正在做什么。

于 2012-12-19T06:41:11.987 回答
24

如果您IList<T>是一个数组 ( T[]),那么您对它们有Array.ForEach方法,类似于ForEachon List<T>IList<T>您可以为您的自定义或IEnumerable<T>您喜欢的任何内容创建扩展方法。

public static void ForEach<T>(this IList<T> list, Action<T> action)
{
    foreach (T t in list)
        action(t);
}

您只需要警惕原始集合中的对象将被修改的事实,但我想命名确实暗示了这一点。

-------------------------------------------------- -------------------------------------------------- --------------------------------

我更喜欢打电话:

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .ForEach(n => AssignCitizenShip(n);

foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
    AssignCitizenShip(n);
}

如果是这样,您可以在IEnumerable. 请注意,终止呼叫会ForEach执行Linq查询。如果你不想要它,你也可以通过使用yield语句并返回一个IEnumerable<T>返回来推迟它:

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach (T t in list)
    {
        action(t);
        yield return t;
    }
}

这解决了副作用问题,但我个人喜欢一个名为ForEach最终执行调用的方法。

-------------------------------------------------- -------------------------------------------------- -------------------------------------------

为了解决关于偏好的相反观点,这里有一个来自 Eric Lippert 的比这个更好的链接。引用他的话:

“第一个原因是这样做违反了所有其他序列运算符所基于的函数式编程原则。显然,调用此方法的唯一目的是产生副作用。表达式的目的是计算一个值,不引起副作用。语句的目的是引起副作用。这个东西的调用站点看起来很像一个表达式(虽然,不可否认,由于该方法是返回无效的,表达式只能在“语句表达式”上下文中使用。)我不喜欢创建唯一一个仅对其副作用有用的序列运算符。

第二个原因是这样做给语言增加了零新的表现力”。

Eric 并不是说​​这是一件坏事——只是决定默认不包含该构造的背后的哲学原因。Linq如果您认为 an 上的函数不IEnumerable应该作用于内容,那么不要这样做。就我个人而言,我不介意它,因为我很清楚它的作用。我将其视为对集合类造成副作用的任何其他方法。如果需要,我也可以进入该函数并对其进行调试。这是另一个来自Linq它自己的东西。

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .AsParallel()
      .ForAll(n => AssignCitizenShip(n);

正如我要说的,这些没有什么不好的。它只是个人喜好。我不会将它用于嵌套foreach的 s 或者如果它涉及在foreach循环内执行的多行代码,因为那是完全不可读的。但是对于我发布的简单示例,我喜欢它。看起来干净简洁。

编辑:查看性能链接顺便说一句:为什么 List<T>.ForEach 比标准 foreach 快?

于 2012-12-19T06:35:08.613 回答
8

您可以制作一个扩展方法并使用void List<T>.ForEach(Action<T> action). 您可以在Shared Source Initiative站点下载源代码。

基本上你会结束这样的事情:

public static void ForEach<T>(this IList<T> list, Action<T> action) 
{
    if (list == null) throw new ArgumentNullException("null");
    if (action == null) throw new ArgumentNullException("action");

    for (int i = 0; i < list.Count; i++)
    {
        action(list[i]);
    }
}

它比使用该foreach语句的其他实现稍好,因为它利用了 IList 包含索引器的事实。

虽然我同意 OR Mapper 的回答,但有时在有许多开发人员的大型项目中,很难让每个人相信一个foreach陈述更清晰。更糟糕的是,如果您的 API 基于接口 (IList) 而不是具体类型 (List),那么习惯于使用的开发人员List<T>.ForEach method可能会开始调用ToList您的 IList 引用!我知道,因为它发生在我之前的项目中。我在遵循框架设计指南的公共 API 中到处使用集合接口。我花了一段时间才注意到许多不习惯这一点的开发人员ToList开始以惊人的速度出现。最后,我将此扩展方法添加到每个人都在使用的通用程序集中,并确保所有不必要的调用ToList已从代码库中删除。

于 2012-12-20T12:57:30.247 回答
3

将此代码添加到静态类并将其称为扩展:

public static void ForEach<T>(this IList<T> list, Action<T> action) {
    foreach(var item in list) {
        action.Invoke(item);
    }
}
于 2012-12-19T06:35:45.517 回答