Почему .ForEach() находится в IList<T>, а не в IEnumerable<T>?[дубликат]
-
03-07-2019 - |
Вопрос
Возможный дубликат:
Почему в интерфейсе IEnumerable нет метода расширения ForEach?
При написании кода LINQ-y я заметил, что .ForEach()
это хорошая идиома для использования.Например, вот фрагмент кода, который принимает следующие входные данные и выдает следующие выходные данные:
{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";
И код:
private string InsertCommasAttempt(IEnumerable<string> words)
{
List<string> wordList = words.ToList();
StringBuilder sb = new StringBuilder();
var wordsAndSeparators = wordList.Select((string word, int pos) =>
{
if (pos == 0) return new { Word = word, Leading = string.Empty };
if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
return new { Word = word, Leading = ", " };
});
wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
return sb.ToString();
}
Обратите внимание на вставленное .ToList()
перед .ForEach()
на предпоследней строке.
Почему это так .ForEach()
недоступен в качестве метода расширения на IEnumerable<T>
?Такой пример кажется странным.
Решение
Потому что ForEach(Action)
существовал раньше IEnumerable<T>
существовал.
Поскольку он не был добавлен вместе с другими методами расширения, можно предположить, что разработчики C# сочли это плохим дизайном и предпочли foreach
построить.
Редактировать:
Если вы хотите, вы можете создать свой собственный метод расширения, он не будет переопределять метод для List<T>
но он будет работать для любого другого класса, который реализует IEnumerable<T>
.
public static class IEnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (T item in source)
action(item);
}
}
Другие советы
По мнению Эрика Липперта, это в основном по философским причинам.Вам следует прочитать весь пост, но вот суть, насколько я понимаю:
Я философски против предоставления такого метода по двум причинам.
Первая причина заключается в том, что это нарушает принципы функционального программирования, на которых основаны все другие операторы последовательности.Очевидно, что единственной целью вызова этого метода является создание побочных эффектов.
Цель выражения состоит в том, чтобы вычислить значение, а не вызвать побочный эффект.Цель утверждения - вызвать побочный эффект.Сайт вызовов этой штуки был бы очень похож на выражение (хотя, по общему признанию, поскольку метод возвращается к пустоту, выражение может использоваться только в контексте «выражения заявления».)
Меня не очень хорошо сочетается с одним и единственным оператором последовательности, который полезен только для его побочных эффектов.
Вторая причина заключается в том, что это добавляет новой новой репрезентативной силы к языку.
Потому что ForEach()
в IEnumerable является обычным явлением для каждого такого цикла:
for each T item in MyEnumerable
{
// Action<T> goes here
}
Я просто предполагаю, но размещение foreach в IEnumerable приведет к тому, что операции над ним будут иметь побочные эффекты.Ни один из «доступных» методов расширения не вызывает побочных эффектов, поэтому использование там императивного метода, такого как foreach, я думаю, запутало бы API.Кроме того, foreach инициализирует ленивую коллекцию.
Лично я отбивался от искушения просто добавить свои собственные, просто чтобы отделить функции без побочных эффектов от функций с побочными эффектами.
ForEach находится не в IList, а в списке.В своем примере вы использовали конкретный список.
Я, честно говоря, не знаю наверняка, почему .ForEach(Action) не включен в IEnumerable, но, верно, неправильно или безразлично, так оно и есть...
Однако я ДЕЙСТВИТЕЛЬНО хотел подчеркнуть проблему производительности, упомянутую в других комментариях.Производительность снижается в зависимости от того, как вы зацикливаете коллекцию.Оно относительно незначительное, но, тем не менее, оно, безусловно, существует.Вот невероятно быстрый и небрежный фрагмент кода, демонстрирующий взаимосвязь...Чтобы пройти, требуется всего минута или около того.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Loop timing test: loading collection...");
List<int> l = new List<int>();
for (long i = 0; i < 60000000; i++)
{
l.Add(Convert.ToInt32(i));
}
Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("foreach loop test starting...");
DateTime start = DateTime.Now;
//l.ForEach(x => l[x].ToString());
foreach (int x in l)
l[x].ToString();
Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("List.ForEach(x => x.action) loop test starting...");
start = DateTime.Now;
l.ForEach(x => l[x].ToString());
Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("for loop test starting...");
start = DateTime.Now;
int count = l.Count();
for (int i = 0; i < count; i++)
{
l[i].ToString();
}
Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("\n\nPress Enter to continue...");
Console.ReadLine();
}
Однако не стоит слишком зацикливаться на этом.Производительность — это валюта проектирования приложений, но если ваше приложение не испытывает реального снижения производительности, вызывающего проблемы с удобством использования, сосредоточьтесь на кодировании для удобства сопровождения и повторного использования, поскольку время — это валюта реальных бизнес-проектов...
ForEach реализован в конкретном классе List<T>
Это всего лишь предположение, но List может перебирать свои элементы без создания перечислителя:
public void ForEach(Action<T> action)
{
if (action == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
action(this._items[i]);
}
}
Это может привести к повышению производительности.С IEnumerable у вас нет возможности использовать обычный цикл for.
Называется "Выбрать" Я просветлен, спасибо.IEnumerable<T>
LINQ следует модели извлечения, и все ее методы (расширения) должны возвращать результат. IEnumerable<T>
, за исключением ToList()
.А ToList()
есть конец тяговой цепи.
ForEach()
из мира пуш-моделей.
Как отметил Сэмюэл, вы все равно можете написать для этого свой собственный метод расширения.