Как стать автором
Обновить

Комментарии 57

Listlist = new List();
list.ForEach(delegate(string some_string)
{
Console.WriteLine(some_string);
});

так вроде бы короче и проще…
если я коненчо правильно понял то чего хотел достичь автор.
хабр сьел всё самое вкусное
List<string> list = new List<string>();
list.ForEach(delegate(string some_string)
{
   Console.WriteLine(some_string);
});


* This source code was highlighted with Source Code Highlighter.
просто лист это лист, а не произвольная коллекция.
А делать каждый раз toList, чтобы применить какой-либо метод не очень кошерно.

В вашем случае будет выглядить так
new []{«one»,«two»}.ToList().ForEach(x => Console.WriteLine(x));

Кроме того, что это чуть более громоздко мы получаем создание ещё одного листа. Это раз. Вроде на спичках экономить глупо, но лишними не будут.
Два — если мы работает с бесконечными списками, то подобные конструкции к ним вообще не будут применимы.
EachLazy же будет вполне адекватно работать.
а чего хотел автор?
Вы из вертикальной плоскости перешли на горизонтальную, шило на мыло.
Сравните:

foreach(var element in collection)
{
     Console.WriteLine(element);
}
и
collection.Each(Console.WriteLine);

Неужто разраслось?
У меня не компилируется этот код. Подскажите где ошибка?
Нашёл ошибку. У меня в проекте
Each(this IEnumerablelist, Action<T,int> func)
назван немного по другому.

А в написанной схеме получается, что
collection.Each(Console.WriteLine);
не может понять какой из Each и WriteLine использовать. Так как WriteLine может быть скастен и к Action<T,int> и к Action.

[гадание на кофейной гуще]
Кроме того, Each должен быть элементов паблик статик класса, чтобы использовать его как Экстеншен метод, компиляция должна проходить под третьим C# и должен быть установлен третий дот нет.
Вы не правильно меня поняли. Моя вина.
Привели неверный код как аргумент товарищу jeje.
Вот правильный вариант:

  1. foreach(var element in collection)
  2. {
  3.    Console.WriteLine(element);
  4. }
  5.  
  6. collection.Each(element => Console.WriteLine(element));
* This source code was highlighted with Source Code Highlighter.


В нем видно, что Ваш код вырос в ширине, причем существенно, а это только вывод в консоль.

Абсолютно работающий код. Зачем писать лямбду, если само Console.WriteLine уже есть Action <string>?
Скомпилируйте и запустите.

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
  public static class CollectionHelpers
  {
    public static IEnumerable<T> Each<T>(this IEnumerable<T> list, Action<T> func)
    {
      if (func == null)
      {
        return list;
      }
      if (list == null)
      {
        return null;
      }
      foreach (var elem in list)
      {
        func(elem);
      }
      return list;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var e = new[] {"test", "two", "3"};
      e.Each(Console.WriteLine);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.
Можно просто в одну строчку и без пробелов. Тоже оптимизация.
Не совсем.
Дело в том, что некоторые используют такую глупую вещь, как programming style, отклоняться от которой нехорошо.
Хм, coding style используется (в основном, конечно) в командах и имеет главную цель — улучшить читаемость кода другими разработчиками. А вы решили, что стандартный (!) foreach вам громоздок и сделали свой — в итоге цель coding style разрушена, каждый, кто будет читать ваш код, будет разбираться, что это за Each вызовы такие и будет продолжать писать по своему, т.е. теряется однообразие кода. Вкратце — я не понимаю конечную цель, вы пишете про coding style, но чтобы его соблюсти разрушаете читаемость программы.
Помимо критики — эксперименты это всегда хорошо :) В принципе, интересно получилось, если не говорить про практическое применение :)
Скорее ваше утверждение — глупость. Программы пишутся в первую очередь для людей и во вторую — для компьютеров.
Вы сделил вещь, никому не нужную кроме вас самих. Шансов что кто-то будет использовать — ноль.
Почему же, лично я заметил для себя, что когда тело цикла foreach достаточно большое, если вынести его в отдельный метод, а сам цикл заменить вызовом одного метода, то читаемость программы сильно улучшается. Просто смотришь и понимаешь, «тут для всех элементов коллекции делается то-то», а что делается не важно, а если важно, можно подробнее восмотреть, что именно делается.
да будут использовать, те, кто знает, что это такое, и пока знают :)

а если через полгода придётся поддерживать ваш код новому разработчику, или использовать куски кода в другом проекте ("глянь там у Петровича, он, вроде делал такое в прошлом году"), то ему придётся долго разгадывать, что вы имели ввиду :)

я сам сейчас стараюсь писать проще:
if (a)
{
}
else
{
}
вместо
a?b:c
пусть больше текста, зато понятно
использование отдельного класса для цикла просто не нужно…
ради того чтобы код выглядел компактнее? а кто же будет думать о производительности?
иногда просто необходим процедурный подход… и мне кажется, он необходим именно в этом случае.
какой процедурный подход, о чем вы сейчас?
А зачем изобретать велосипед, если есть LINQ и Select?
Разница в том, что
1) Select всегда является ленивым. В некоторых случаях это не очень удобно.
2) Each является мутатором, а селект нет. То есть Each работает с той-же коллекцией, а Select создаёт новую.

Пример.
radioButtonList1.Items.Cast<ListItem>().Each(x => x.Selected = true);
В случае селекта нужно бы было писать
radioButtonList1.Items.Cast<ListItem>().Select(x => x.Selected = true).ToList();

А вот такое выражение
Each(x => {x.Selected = true; x.Text+="_test";});
будет эквивалентно переписано как
Select(x => { x.Text += "_test"; return x.Selected = true; }).ToList();
или даже
Select(x => { x.Text += "_test"; x.Selected = true; return x; }).ToList();

Вообще весь этот код появился из-за того, что в Linq нет методов, которые работают с Аction вместо Func.
C ленивостью и созданием новой коллекции — Ваша правда :)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
метод Select для IEnumerable msdn.microsoft.com/en-us/library/bb548891.aspx

public static class Sequence{
      public static IEnumerable<S> Select<T,S>(
            this IEnumerable<T> source, Func<T,S> selector)
      {
            foreach (T element in source) yield return selector(element);
      }
      public static IEnumerable Select<T,S>(
            this IEnumerable source, Func<T,S> selector)
      {
            foreach (T element in source) yield return selector(element);
      }
}


* This source code was highlighted with Source Code Highlighter.
А если нужен break;
Или внешняя переменная?

Возращаться к обычному foreach?
Т.е. написать раз, потом через какое-то время надо изменить, не получается и переписывать второй?
Внешнюю переменную можно использовать без проблем.
С break действительно не получится.
Просто если есть многострочный форич, внутренность которого занимает десяток строк, то помещать его в лямбду не совсем логично.

Иногда break можно заменить на source.TakeWhile(func1).Each(funk2).
Понятно, что Each не претендует на полноту конструкции и используется только как дополнительный сахар.

Например вместо if можно использовать ?, но везде где только можно использовать его не надо.
В моей случае ситуация аналогична.
Вместо break можно добавить обработку специально EachCancelException.

Это будет слишком ресурсоемко. Вы привели как раз неправильный пример использования исключений.
Лучше все же не стараться реализовать break (а ведь есть еще и continue!) в extension-методе, а использовать обычный foreach.
Кроме того, отладка extension-метода будет страдать.
>>использовать обычный foreach
Вопрос не в том использовать, или не использовать… но если мы хотим не использовать foreach (дааа это бооольшой вопрос стоит ли делать так как предлагает автор этого топика), то выход я вижу один — исключения.

>>Это будет слишком ресурсоемко
Насколько? Кто-нить мерил break vs exceptions?

>>неправильный пример использования исключений
Другий способ нелокального контроля управления в случае использования делегатов — коды возврата, еще хуже.

Тяжело без функционального программирования, да.
Ага, в C можно так:

#define array_foreach(d,a) if(a)for((a)->iterator=0,(d)=array_fetch((a),(a)->iterator);(a)->iterator<(a)->items;(a)->iterator++,(d)=array_fetch((a),(a)->iterator))

ARRAY *a,*c;
unsigned long d;
array_foreach(d,a)
{
 array_push(c,d);
}

* This source code was highlighted with Source Code Highlighter.
НЛО прилетело и опубликовало эту надпись здесь
Лёгким движением руки и такой-то матерью C# понемногу превращается в Ruby.
Господи, я всегда думал что Хаскель стоит менять на C# именно для отсутствия таких конструкций.
Ну и for надо тоже на экстеншн заменить… для «лучшей» читаемости
public static IEnumerable<T> For0<T>(this int i, Func<int, T> fnc)
{
  for(var j=0;j<i;j++)
    yield return fnc(j);
}


* This source code was highlighted with Source Code Highlighter.
НЛО прилетело и опубликовало эту надпись здесь
Было много, но понятно — стало мало и менее понятно.
Главное в больших проэктах вот такого не делать.
Порадовался за автора, особенно когда он воткнул i++ в цикл foreach.

«Он хотел спросить „Месье не рехнулся?“, но оттренированная годами выдержка взяла свое.
— Месье уверен?
— Да, конечно. Месье знает толк в извращениях,- успокоил его я.» :)
Не использовали вы бы foreach, да еще с i++. Он сам по себе создает свой итератор и с ним возится. В любом случае, вся это бнопня работает медленнее чем for. Можете в ildasm кстати посмотреть что там генерится))
Выберите другой язык.
Ruby:

collection = [1, 2, 3, 4, 5]
collection.each{ |item| puts item }
collection = collection.collect{ |item| item * 10 } # [2, 4, 6, 8, 10]
collection_filtered = collection.select{ |item| item > 2 } # [3, 4, 5]
sum = collection.inject(0){ |item, sum| sum += item } # 15
Зачем C# правращать в JavaScript и тому-подобные языки программирования, чем больше используется стандартных решений, тем проще воспринимается программный код, а несколько строк вместо одной — это не грмоздкость.
Взгляните на Nemerle, порадуетесь.
Хм… Изобретаем map? Даёшь fodl, foldr и filter!
Map это Select в линку.
Where — filter
Aggregate — foldl

Так что не совсем его изобретаю. Да и смысл изобретать изобретённое.
Мне кажется, что читабельность и сложность кода от этого только выросли. Сравнимая ли это цена за экономию нескольких строчек? Не уверен.
В качестве just for fun интересно почитать, но использовать не буду :).
Вот уж не думаю, что красивее…

Вы просто сейчас в экстазе сокращения сверх вниз, но через месяц, а давайте возьмём год! Через Вы будете читать этот код и вспоминать, а чтобы это значило?

Либо кто-то другой вместо привычного foreach`а будет видеть плоский perl`о-подобный код.

Так что здесь весьма своеобразное понятие о красоте.

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

Ваши критерии:
— вертикальная плоскость кода

Мои ортодоксальные критерии:
— привычность кода, ибо его чаще читают, нежели пишут
— производительность кода

См. книжку «Идеальный код».
Было интересно. А что с производительностью?

msmvps.com/blogs/jon_skeet/archive/2009/01/29/for-vs-foreach-on-arrays-and-lists.aspx
Что-то очень странное пишет там автор.
Сравнил эту обёртку, стандартный форич и аггегэйт на примере суммирования, как у автора.

В результате оказалось, что быстрее всего работает стандартный форич, примерно в 2-2.5 раза ему проигрывает моя обёртка, аггрегэйт отстаёт ещё на 15-30%.
Что странного? Там тоже стандарные for и foreach быстрее
Не буду оригинален, если скажу, что другой программист, увидев это впервые в коде, будет вынужден лезть в определение и некоторые время с недоверием всматриваться, пытаясь понять, в чем же подвох и зачем же это было сделано :)

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

А как идея, это конечно прикольно.
Все хорошо, все красиво, читабельно.
Возникает один вопрос — или мы используем форыч, который в конечном итоге мутирует в класс итератор и цикл while, или мы создаем какой-то там экстеншн, который в себе помимо форыча еще дофига всего в себе содержит. Я понимаю, что сидеть и дуреть из-за быстродействия и всяких там расходов памяти при нынешней то ситуации не слишком разумно, но все же возникает вопрос — нафига все вышеописанное нужно?

Резюмирую: имхо — в общем случае бесполезно.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории