Подтвердить что ты не робот

Как выйти из цикла List <string>.ForEach при использовании анонимного делегата?

В нормальном цикле вы можете выйти из цикла, используя break. Можно ли это сделать с помощью анонимного делегата?

Пример inputString и результат объявляются вне делегата.

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

Изменить: Спасибо за ответы, я на самом деле читаю твою книгу на минуту Джон. Просто для записи я попал в эту проблему и переключился на обычный цикл foreach, но я разместил этот вопрос, чтобы увидеть, что я что-то пропустил.

4b9b3361

Ответ 1

Как и другие, вы не можете выйти из цикла в ForEach.

Можете ли вы использовать LINQ? Если это так, вы можете легко объединить TakeWhile и настраиваемый метод расширения ForEach (который примерно каждый проект, кажется, имеет эти дни).

В вашем примере, однако, List<T>.FindIndex будет лучшей альтернативой, но если вы на самом деле этого не делаете, пример того, что вы действительно хотите сделать.

Ответ 2

Нет никакого цикла, к которому у пользователя есть доступ, из которого можно разбить. И каждый вызов (анонимный) делегат - это новый вызов функции, поэтому локальные переменные не помогут. Но поскольку С# дает вам закрытие, вы можете установить флаг, а затем ничего не делать при последующих вызовах:

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;

Если вы хотите запустить дополнительную логику, есть вещи, которые вы можете сделать (с LINQ) с помощью TakeWhile и т.д.

Ответ 4

Я не думаю, что есть элегантный способ сделать это при использовании метода ForEach. Хакерное решение - это исключение.

Что мешает вам делать старомодный foreach?

foreach (string item in blackList)
{
    if (!inputString.Contains(item)) continue;

    result = true;
    break;
}

Ответ 5

Единственный способ "выйти" из цикла - выбросить исключение. Не существует способа "разбить" стиль выхода из метода .ForEach, как и обычный цикл foreach.

Ответ 6

Если вам нужен цикл, используйте цикл.

Action не допускает никакого возвращаемого значения, поэтому функция ForEach не может знать, что вы хотите сломать, за исключением исключения. Использование исключения здесь является излишним.

Ответ 7

Метод ForEach не предназначен для этого. Если вы хотите узнать, содержит ли коллекция элемент, вы должны использовать метод Contains. И если вы хотите выполнить проверку всех элементов в коллекции, вы должны попробовать любой метод расширения.

Ответ 8

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

Ответ 9

bool @break = false;

blackList.ForEach(item =>
 {  
    if([email protected] && inputString.Contains(item))
     { @break = true;
       result = true;
     }

    if (@break) return;
    /* ... */
 });

Обратите внимание, что вышеперечисленное по-прежнему будет проходить через каждый элемент, но сразу же возвращается. Конечно, этот способ, вероятно, не так хорош, как обычный foreach.

Ответ 10

Если вы действительно хотите создать цикл 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();
    }
}

надеюсь, что эта помощь поможет получить другую точку зрения.

Ответ 11

Будет ли это работать для вас:

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