是否有类似于ForEach的方法可用于IList?

49

可能重复:
IEnumerable<T>的LINQ等价于foreach循环

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个回答

51

使用 foreach 循环:

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

如果使用委托和扩展方法并没有真正提高可读性,则没有理由在各个地方都使用它们;使用 foreach 循环并不比使用 ForEach 方法更难以理解。


26
如果您的 IList<T> 是一个数组 (T[]),那么您可以像对 List<T> 使用 ForEach 方法一样,对其使用类似的 Array.ForEach 方法。 您可以为自定义的 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);

than

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的更好的链接,而不是这个。引用他的话:
“第一个原因是这样做违反了所有其他序列运算符基于的函数式编程原则。显然,调用此方法的唯一目的是导致副作用。表达式的目的是计算值,而不是导致副作用。语句的目的是导致副作用。这个东西的调用站点看起来非常像表达式(尽管可以承认,由于该方法是void返回的,表达式只能在“语句表达式”上下文中使用。)使得仅有的一个序列操作符仅对其副作用有用,这让我感到不舒服。

第二个原因是这样做并未为语言增加任何新的表示能力。”

Eric并不是说这样做是坏事-只是默认情况下不在Linq中包含该结构的决定背后的哲学原因。如果您认为IEnumerable上的函数不应对其内容进行操作,则不要这样做。个人而言,我并不介意它,因为我很清楚它的作用。我将其视为对集合类执行副作用的任何其他方法。我也可以进入函数并进行调试。这里还有一个来自Linq本身的例子。
people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .AsParallel()
      .ForAll(n => AssignCitizenShip(n);

正如我所说的,这些东西并没有什么不好。这只是个人偏好。如果涉及嵌套的foreach或需要在foreach循环内执行超过一行的代码,则会变得难以阅读。但对于我发布的简单示例,我喜欢它。看起来干净简洁。 编辑: 还可以查看性能链接:为什么List<T>.ForEach比标准foreach更快?

8
你可以创建一个扩展方法,使用大部分 void List<T>.ForEach(Action<T> action) 的实现。你可以在共享源代码计划网站下载源代码。

基本上,你会得到类似于以下的内容:

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方法的开发人员可能会开始在您的IList引用上调用ToList!我知道,因为它曾经在我的上一个项目中发生过。我在我们公共API的各个地方都使用集合接口,遵循Framework Design Guidelines。我花了一段时间才注意到很多开发人员不习惯这种做法,并且开始以惊人的速度调用ToList。最后,我将这个扩展方法添加到一个所有人都在使用的通用程序集中,并确保从代码库中删除了所有不必要的ToList调用。


3

将以下代码添加到静态类中,并将其称为扩展:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接