Отправка элементов последовательности 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 вернет последовательность логических значений — я просто проигнорирую эту последовательность.Однако это кажется немного нубским и глупым.Есть ли какой-нибудь элегантный способ сделать это?Или я просто неправильно применяю LINQ?
Спасибо.
Решение
Прежде всего, ваш текущий код не будет работать.
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;
}
Другие советы
По общему мнению, LINQ предназначен для запроса и выбора...пока вы используете традиционные итеративные методы для циклов и итераций.
Ненавижу это говорить, но вы бы использовали традиционный цикл foreach, потому что хотите, чтобы ваши запросы linq выполнялись, и вы перебираете полученный IEnumerable.Это помогает улучшить читаемость кода, и я признаю, что LINQ вызывает привыкание.Вы хотите делать все, используя Lambdas и отложенное выполнение, но цикл следует оставить на усмотрение традиционных методов цикла C#.Это определенно поможет справиться с побочными эффектами.