Отправка элементов в последовательности LINQ методу, который возвращает void

Часто, когда я имею дело с последовательностями LINQ, я хочу отправить каждый элемент методу, возвращающему void, избегая цикла foreach. Однако я не нашел элегантный способ сделать это. Сегодня я написал следующий код:

    private StreamWriter _sw;
    private void streamToFile(List<ErrorEntry> errors)
    {
        if (_sw == null)
        {
            _sw = new StreamWriter(Path.Combine
                                    (Path.GetDirectoryName(_targetDatabasePath), "errors.txt"));
        }

        Func<ErrorEntry, bool> writeSelector = 
            (e) => { _sw.WriteLine(getTabDelimititedLine(e)); return true; };

        errors.Select(writeSelector);

        _sw.Flush();
    }

Как вы можете видеть, я пишу функцию лямбда, которая возвращает true, и я понимаю, что метод Select вернет последовательность логических значений - я просто проигнорирую эту последовательность. Однако, это кажется немного noobish и jank. Есть ли элегантный способ сделать это? Или я просто неправильно использую LINQ?

Спасибо.

Ответ 1

Прежде всего, ваш текущий код не будет работать.
Select и большинство других методов LINQ, используйте отложенное выполнение, что означает, что они фактически ничего не делают, пока вы не перечислите результаты.

В общем, вы никогда не должны использовать лямбда с побочными эффектами в запросе LINQ.

Чтобы ответить на ваш вопрос, вы должны использовать цикл foreach.

Вы ищете метод расширения foreach; Эрик Липперт объясняет, почему Microsoft не написала ни одного.

Если вы действительно этого захотите, вы можете написать один:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action) {
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (action == null) throw new ArgumentNullException("action");
    foreach(T item in sequence) 
        action(item);
}

//Return false to stop the loop
public static void ForEach<T>(this IEnumerable<T> sequence, Func<T, bool> action) {
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (action == null) throw new ArgumentNullException("action");

    foreach(T item in sequence) 
        if (!action(item))
            return;
}

Ответ 2

Общий консенсус в том, что LINQ предназначен для запросов и выбора..., в то время как вы используете традиционные итерационные методы для циклирования и итерации.

Мне не нравится это говорить, но вы бы использовали традиционный цикл foreach, потому что вы хотите, чтобы ваши запросы linq выполнялись, и вы перебираете полученный IEnumerable. Это помогает с читабельностью кода, и я соглашусь, что LINQ вызывает привыкание. Вы хотите сделать все, используя Lambdas и отложенное выполнение, но цикл должен быть оставлен вашим традиционным методам циклирования С#. Это, безусловно, поможет с побочными эффектами.