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

Почему .ForEach() на IList <T>, а не на IEnumerable <T>?

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

4b9b3361

Ответ 1

Потому что ForEach(Action) существовал до IEnumerable<T>.

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


Edit:

Если вы хотите создать свой собственный метод расширения, он не будет переопределять значение для 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);
  }
}

Ответ 2

По словам Эрика Липперта, это в основном по философским соображениям. Вы должны прочитать весь пост, но вот суть, насколько мне известно:

Я философски возражаю против предоставление такого метода для двух причины.

Первая причина в том, что так нарушает функциональное программирование принципы, что вся другая последовательность операторы основаны на. Ясно, что единственная цель вызова этого метода является причиной побочных эффектов.

Цель выражения - вычислить значение, а не вызывать сторону эффект. Цель заявления - чтобы вызвать побочный эффект. Сайт вызова этой вещи было бы ужасно как выражение (хотя, по общему признанию, поскольку метод void-return, выражение могло бы используется только в заявлении выражение ".)

Мне не очень нравится делать оператор единственной последовательности это полезно только для его стороны эффекты.

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

Ответ 3

Потому что ForEach() в IEnumerable является нормальным для каждого цикла следующим образом:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

Ответ 4

Я просто угадываю здесь, но установка foreach на IEnumerable заставит операции на нем иметь побочные эффекты. Ни один из "доступных" методов расширения не вызывает побочных эффектов, накладывая императивный метод, подобный foreach, там будет muddy api, я думаю. Кроме того, foreach инициализирует ленивую коллекцию.

Лично я отбивался от соблазна просто добавить свои собственные, просто чтобы сохранить побочные эффекты свободных функций отдельно от тех, у кого есть побочные эффекты.

Ответ 5

ForEach не включен в список. Вы использовали конкретный список в своем примере.

Ответ 6

Я честно не знаю наверняка, почему .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();
    }

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

Ответ 7

ForEach реализуется в конкретном классе List<T>

Ответ 8

Он называется "Выбрать" на IEnumerable<T> Я просвещен, спасибо.

Ответ 9

Просто догадаться, но 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 у вас нет возможности использовать обычный для цикла.

Ответ 10

LINQ следует модели pull, и все ее методы (расширения) должны возвращать IEnumerable<T>, за исключением ToList(). ToList() существует, чтобы закончить тянущую цепь.

ForEach() - из мира модели push.

Вы все равно можете написать свой собственный метод расширения, чтобы сделать это, как указал Самуил.