【问题标题】:Is there any method like ForEach for IList? [duplicate]有没有类似 ForEach for IList 的方法? [复制]
【发布时间】:2012-12-06 11:19:42
【问题描述】:

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

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

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

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

但是如果names 不是List&lt;T&gt; 而是IList&lt;T&gt; 怎么办? IList&lt;T&gt; 没有像 ForEach 这样的方法。

还有其他选择吗?

【问题讨论】:

    标签: c# linq extension-methods


    【解决方案1】:

    使用foreach 循环:

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

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

    【讨论】:

      【解决方案2】:

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

      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&lt;T&gt; 来推迟它:

      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);
      

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

      编辑:顺便查看性能链接:Why is List<T>.ForEach faster than standard foreach?

      【讨论】:

        【解决方案3】:

        您可以创建一个扩展方法并使用void List&lt;T&gt;.ForEach(Action&lt;T&gt; 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&lt;T&gt;.ForEach method 的开发人员可能会开始在您的 IList 引用上调用 ToList!我知道,因为它发生在我之前的项目中。我在 Framework Design Guidelines 之后的公共 API 中到处使用集合接口。我花了一段时间才注意到许多不习惯这种情况并致电ToList 的开发人员开始以惊人的速度出现。最后,我将此扩展方法添加到每个人都在使用的通用程序集中,并确保从代码库中删除所有对 ToList 的不必要调用。

        【讨论】:

          【解决方案4】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多