【问题标题】:How do i exit a List<string>.ForEach loop when using an anonymous delegate?使用匿名委托时如何退出 List<string>.ForEach 循环?
【发布时间】:2009-02-17 14:47:57
【问题描述】:

在正常循环中,您可以使用 break 跳出循环。使用匿名委托也可以做到这一点吗?

示例 inputString 和 result 都在委托之外声明。

blackList.ForEach(new Action<string>(
    delegate(string item)
    {
        if(inputString.Contains(item)==true)
        {
            result = true;
            // I want to break here
        }
    }
));

编辑: 感谢您的回复,我实际上正在阅读您的书 John :) 只是为了记录,我遇到了这个问题并切换回正常的 foreach 循环,但我发布了这个问题,看看我是否遗漏了什么。

【问题讨论】:

标签: c# loops delegates


【解决方案1】:

正如其他人所发布的,您无法在 ForEach 中退出循环。

你会使用 LINQ 吗?如果是这样,您可以轻松地结合 TakeWhile 和自定义 ForEach 扩展方法(现在几乎每个项目似乎都有)。

然而,在您的示例中,List&lt;T&gt;.FindIndex 将是最佳选择 - 但如果您实际上并未这样做,请发布您真正想要做的示例。

【讨论】:

    【解决方案2】:

    没有一个可以访问的循环,可以从中中断。并且对(匿名)委托的每次调用都是一个新的函数调用,因此局部变量将无济于事。但是由于 C# 给了你一个闭包,你可以设置一个标志,然后在进一步的调用中什么也不做:

    bool stop = false;
    myList.ForEach((a) => {
      if (stop) {
        return;
      } else if (a.SomeCondition()) {
        stop = true;
      }
    });
    

    (这需要测试以检查是否生成了正确的闭包引用语义。)

    更高级的方法是创建您自己的扩展方法,允许委托返回 false 以停止循环:

    static class MyExtensions {
      static void ForEachStoppable<T>(this IEnumerable<T> input, Func<T, bool> action) {
        foreach (T t in input) {
          if (!action(t)) {
            break;
          }
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      您有可用的 LINQ 吗?您的逻辑似乎类似于 Any:

      bool any = blackList.Any(s=>inputString.Contains(s));
      

      等同于:

      bool any = blackList.Any(inputString.Contains);
      

      如果你没有 LINQ,那么这仍然是一样的:

      bool any = blackList.Find(inputString.Contains) != null;
      

      如果你想运行额外的逻辑,你可以用TakeWhile等做一些事情(使用LINQ)

      【讨论】:

      • 我建议使用 FindIndex 而不是 Find,因为它提供了一种更通用的方法来检测“未找到条目”。
      • +1,这里的问题似乎不是 .ForEach() 的问题,它正在为工作找到正确的方法。
      • @Jon - true - 更容易检测到非类。不过,我会按原样离开,并为你的 +1 ;-p
      【解决方案4】:

      我认为在使用 ForEach 方法时没有一种优雅的方法可以做到这一点。一个 hacky 解决方案是抛出异常。

      是什么阻止您执行老式的 foreach?

      foreach (string item in blackList)
      {
          if (!inputString.Contains(item)) continue;
      
          result = true;
          break;
      }
      

      【讨论】:

      • 什么都没有,我只是想了解更多关于代表及其局限性的信息。
      • 长了五行! +500%!
      【解决方案5】:

      如果你想要一个循环,就使用一个循环。

      Action 不允许返回值,因此 ForEach 函数不可能知道您想要中断,除非抛出异常。在这里使用异常是多余的。

      【讨论】:

        【解决方案6】:

        “退出”循环的唯一方法是抛出异常。没有像普通 foreach 循环那样退出 .ForEach 方法的“中断”方式。

        【讨论】:

          【解决方案7】:

          ForEach 方法并不是要这样做。如果你想知道一个集合是否包含一个项目,你应该使用 Contains 方法。如果您想对集合中的所有项目执行检查,您应该尝试 Any 扩展方法。

          【讨论】:

            【解决方案8】:
            bool @break = false;
            
            blackList.ForEach(item =>
             {  
                if(!@break && inputString.Contains(item))
                 { @break = true;
                   result = true;
                 }
            
                if (@break) return;
                /* ... */
             });
            

            请注意,上述内容仍会遍历每个项目,但会立即返回。当然,这种方式可能不如普通的foreach

            【讨论】:

              【解决方案9】:
                  class Program
              {
                  static void Main(string[] args)
                  {
                      List<string> blackList = new List<string>(new[] { "jaime", "jhon", "febres", "velez" });
                      string inputString = "febres";
                      bool result = false;
                      blackList.ForEach((item) =>
                                            {
                                                Console.WriteLine("Executing");
                                                if (inputString.Contains(item))
                                                {
                                                    result = true;
                                                    Console.WriteLine("Founded!");
                                                }
                                            },
                                        () => result);
                      Console.WriteLine(result);
                      Console.ReadLine();
                  }
              
              
              }
              public static class MyExtensions
              {
                  public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action, Func<bool> breakOn)
                  {
                      foreach (var item in enumerable)
                      {
                          action(item);
                          if (breakOn())
                          {
                              break;
                          }
                      }
                  }
              }
              

              【讨论】:

                【解决方案10】:

                这对你有用吗:

                bool result = null != blackList.Find( item => inputString.Contains(item)) );
                

                【讨论】:

                  【解决方案11】:
                  blackList.ForEach(new Action<string>(
                      delegate(string item)
                      {
                          if(inputString.Contains(item)==true)
                          {
                              result = true;
                              // I want to break here
                              return;
                          }
                      }
                  ));
                  

                  【讨论】:

                  • 虽然此代码 sn-p 可能是解决方案,但 including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
                  • 解决方案非常简单,恕我直言不需要解释。它只是返回;。我想确定我是对的,但由于有 2 人在这里投了反对票,所以我不确定。但是返回;是正确的答案!
                  【解决方案12】:

                  如果你真的想在一个列表中存在一个循环 foreach,你可以使用如下代码的异常:

                  public class ExitMyForEachListException : Exception
                  {
                      public ExitMyForEachListException(string message)
                          : base(message)
                      {
                      }
                  }
                  class Program
                  {
                      static void Main(string[] args)
                      {
                          List<string> str = new List<string>() { "Name1", "name2", "name3", "name4", "name5", "name6", "name7" };
                          try
                          {
                              str.ForEach(z =>
                              {
                                  if (z.EndsWith("6"))
                                      throw new ExitMyForEachListException("I get Out because I found name number 6!");
                                  System.Console.WriteLine(z);
                              });
                          }
                          catch (ExitMyForEachListException ex)
                          {
                              System.Console.WriteLine(ex.Message);
                          }
                  
                          System.Console.Read();
                      }
                  }
                  

                  希望这有助于获得其他观点。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2015-01-15
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-12-25
                    • 1970-01-01
                    • 2010-10-02
                    相关资源
                    最近更新 更多