Почему .ForEach() находится в IList<T>, а не в IEnumerable<T>?[дубликат]

StackOverflow https://stackoverflow.com/questions/800151

Вопрос

Возможный дубликат:
Почему в интерфейсе 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() из мира пуш-моделей.

Как отметил Сэмюэл, вы все равно можете написать для этого свой собственный метод расширения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top