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

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

Пример inputString и result объявлены вне делегата.

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

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


person SecretDeveloper    schedule 17.02.2009    source источник
comment
возможный дубликат List ForEach break   -  person Ryan Gates    schedule 24.03.2014


Ответы (11)


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

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

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

person Jon Skeet    schedule 17.02.2009

Нет петли, к которой можно было бы выйти, из которой можно было бы вырваться. И каждый вызов (анонимного) делегата — это вызов новой функции, поэтому локальные переменные не помогут. Но так как 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;
      }
    }
  }
}
person Richard    schedule 17.02.2009

У вас есть доступ к 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 и т. д.

person Marc Gravell    schedule 17.02.2009
comment
Я предлагаю FindIndex вместо Find, так как он обеспечивает более общий способ обнаружения ненайденной записи. - person Jon Skeet; 17.02.2009
comment
+1, похоже, проблема здесь не в том, чтобы выйти из .ForEach(), а в том, чтобы найти правильный метод для задания. - person Daniel Schaffer; 17.02.2009
comment
@Jon - правда - легче обнаружить не-классы. Я оставлю все как есть и +1 вашему ;-p - person Marc Gravell; 17.02.2009

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

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

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

    result = true;
    break;
}
person Michael Meadows    schedule 17.02.2009
comment
Абсолютно ничего, я просто хочу узнать больше о делегатах и ​​их ограничениях. - person SecretDeveloper; 17.02.2009
comment
Это на пять строк больше! +500%! - person Thibault D.; 11.03.2016

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

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

person Yuliy    schedule 17.02.2009

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

person JaredPar    schedule 17.02.2009

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

person Rune Grimstad    schedule 17.02.2009

bool @break = false;

blackList.ForEach(item =>
 {  
    if(!@break && inputString.Contains(item))
     { @break = true;
       result = true;
     }

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

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

person Mark Cidade    schedule 17.02.2009

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

bool result = null != blackList.Find( item => inputString.Contains(item)) );
person Andrew Mochalskyy    schedule 04.04.2012

если вы действительно хотите, чтобы в списке существовал цикл 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();
    }
}

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

person Ricardo Figueiredo    schedule 19.02.2016

person    schedule
comment
Хотя этот фрагмент кода может быть решением, включая объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос для будущих читателей, и эти люди могут не знать причин вашего предложения кода. - person Neo Anderson; 15.09.2020