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

Список ForEach break

Есть ли способ вырваться из метода расширения foreach? Ключевое слово "break" не распознает метод расширения как допустимую область для прерывания.

//Doesn't compile
Enumerable.Range(0, 10).ToList().ForEach(i => { System.Windows.MessageBox.Show(i.ToString()); if (i > 2)break; });

Изменить: удалить "linq" из вопроса


Обратите внимание, что код - это просто пример, показывающий, что break не работает в методе расширения... действительно, я хочу, чтобы пользователь мог прервать обработку списка. В потоке пользовательского интерфейса есть прерывная переменная, а для цикл просто ломается, когда пользователь нажимает кнопку отмены. Прямо сейчас, у меня нормальный цикл, но я хотел посмотреть, можно ли сделать это с помощью метода расширения.

4b9b3361

Ответ 1

Вероятнее всего, это будет более точный вызов List<T> Foreach vs. LINQ.

В любом случае, хотя нет никакого способа выйти из этого цикла. Прежде всего потому, что на самом деле это не цикл. Это метод, который принимает делегат, который вызывается внутри цикла.

Создание функции ForEach с возможностью прерывания довольно прямолинейно, хотя

public delegate void ForEachAction<T>(T value, ref bool doBreak);
public static void ForEach<T>(this IEnumerable<T> enumerable, ForEachAction<T> action) {
    var doBreak = false;
    foreach (var cur in enumerable) {
        action(cur, ref doBreak);
        if (doBreak) {
            break;
        }
    }
}

Затем вы можете переписать свой код следующим образом

Enumerable.Range(0,10)
    .ForEach((int i,ref bool doBreak) => {
        System.Windows.MessageBox.Show(i.ToString()); 
        if ( i > 2) {doBreak = true;}
    });

Ответ 2

Я рекомендую использовать TakeWhile.

Enumerable.Range(0, 10).TakeWhile(i => i <= 2).ToList().ForEach(i => MessageBox.Show(i.ToString()));

Или, используя Rx:

Enumerable.Range(0, 10).TakeWhile(i => i <= 2).Run(i => MessageBox.Show(i.ToString()));

Ответ 3

Почему бы не использовать Where?

Enumerable.Range(0, 10).Where(i => i <= 2).ToList().ForEach(...)

Ответ 4

Попробуйте выражение "return" вместо "break"; это функция делегата, а не реальный цикл.

Try:

Enumerable.Range(0, 10).ToList().ForEach(i => { System.Windows.MessageBox.Show(i.ToString()); if (i > 2) return; });

Ответ 5

Почему бы не использовать foreach?...

PS: Просто шучу, и, как вы упомянули, я понимаю идею попытаться понять, как это сделать с Linq (и googled здесь по той же причине)... теперь по причинам обслуживания/готовности в производственном коде угадывают простой foreach иногда бывает достаточно.

Ответ 6

Для себя я использовал Linq Select + FirstOrDefault. Преобразуйте "каждый" в список, но поскольку мы просим First, он перестанет их преобразовывать, найдя первый, который соответствует.

var thingOption = list.Select(thing => AsThingOption(thing))
.FirstOrDefault(option => option.HasValue) ?? Option.None<MyThing>;

Как вы можете видеть, я преобразовываю каждую вещь в опцию, и мой метод AsThingOption может преуспеть в преобразовании, и когда это произойдет, мы прекращаем повторение списка. Если условия внутри метода AsThingOption никогда не преуспевают в преобразовании, конечным результатом является None.

Надеюсь, что это поможет!

Ответ 7

Почему бы не throw new Exception("Specific message") или использовать логический флаг?

DataTable dt = new DataTable();
bool error = false;

try
{
    dt.AsEnumerable().ToList().ForEach(dr =>
    {
        if (dr["Col1"].ToString() == "")
        {
            error = true;
            throw new Exception("Specific message");
        }
        else 
            // Do something
    });
}
catch (Exception ex)
{
    if (ex.Message == "Specific message" || error) 
        // do something
    else
        throw ex;
}

Ответ 8

У меня есть случай, когда мне нужно выполнять операции цикла до тех пор, пока условие не будет ложным, и каждое действие может изменить значение, используемое для отслеживания состояния. Я придумал этот вариант .TakeWhile(). Преимущество здесь в том, что каждый элемент в списке может участвовать в некоторой логике, тогда цикл может быть прерван при условии, не связанном напрямую с элементом.

new List<Action> {
    Func1,
    Func2
}.TakeWhile(action => {
    action();
    return _canContinue;
});