有没有像Forist for IList的方法?

时间:2012-12-19 06:31:20

标签: c#

  

可能重复:
  LINQ equivalent of foreach for IEnumerable<T>

List<T>有一个名为ForEach的方法,它对每个元素执行传递的操作。

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

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

但是如果names不是List<T>而是IList<T>IList<T>没有类似ForEach的方法,那该怎么办?还有其他选择吗?

4 个答案:

答案 0 :(得分:41)

使用foreach循环:

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

如果实际上没有提高可读性,则没有理由在整个地方使用委托和扩展方法;与foreach方法相比,ForEach循环不会明确地告诉读者正在做什么。

答案 1 :(得分:20)

如果您的IList<T>是一个数组(T[]),那么您的Array.ForEach方法与ForEach上的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的here is a better linkthis。引用他:

  

“第一个原因是这样做违反了函数式编程   所有其他序列运算符所基于的原则。   显然,调用这种方法的唯一目的是引起副作用   效果。表达式的目的是计算值,而不是计算值   引起副作用。声明的目的是引起一方   影响。这个东西的呼叫网站看起来很像   表达式(但是,诚然,因为该方法是无效的,   表达式只能用在“语句表达式”中   上下文。)与我合作并不合适   序列运算符仅对其副作用有用。

     

第二个原因是,这样做会增加零代表性   语言的力量“。

Eric并没有说这是一件坏事 - 只是哲学背后的原因,默认情况下决定不在Linq中包含该构造。如果您认为IEnumerable上的某个函数不应对内容起作用,则不要这样做。就个人而言,我不介意,因为我很清楚它的作用。我将其视为导致集合类副作用的任何其他方法。如果我愿意的话,我也可以进入函数并调试它。这是Linq本身的另一个。

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

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

修改:查看性能链接btw:Why is List<T>.ForEach faster than standard foreach?

答案 2 :(得分:6)

您可以制作扩展方法并使用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包含索引器的事实。

虽然我对O. R. Mapper的回答很有帮助,但有时候在很多开发人员的大项目中,很难让每个人都相信foreach陈述更清楚。更糟糕的是,如果您的API基于接口(IList)而不是具体类型(List),那么习惯List<T>.ForEach method的开发人员可能会开始在您的IList引用上调用ToList!我知道,因为它发生在我之前的项目中。我在Framework Design Guidelines之后的公共API中使用了集合接口。我花了一段时间注意到许多不习惯这种情况并且致电ToList的开发人员开始以惊人的速度开始使用。最后,我将此扩展方法添加到每个人都在使用的公共程序集中,并确保从代码库中删除了对ToList的所有不必要的调用。

答案 3 :(得分:2)

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

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