Pull to refresh

Обёртка для foreach

Reading time3 min
Views3.3K
В последнее время меня стала раздражать громоздкость кода, неповоротливые конструкции и наличие лишних строк.
Простой foreach в соответствии с codestyle превращается минимум в 4 строки текста
foreach(var element in collection)
{
  // Do something
}

* This source code was highlighted with Source Code Highlighter.
Поэтому образовалась вот такая обёртка.
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;
}

* This source code was highlighted with Source Code Highlighter.
При проверке аргументов возможно было бы удобнее писать throw new ArgumentNullException, но для моих задач лучше использовать механизм «не получилось, ну и ладно».

Дальше, по мере использования, я натолкнулся на проблему, когда нужно было знать индекс обрабатываемого элемента. Для этого я использовал следующее расширение:
public static IEnumerable<T> Each<T>(this IEnumerable<T> list, Action<T,int> func)
{
  if (func == null)
  {
    return list;
  }
  if (list == null)
  {
    return null;
  }
  int i = 0;
  foreach(var elem in list)
  {
    func(elem, i);
    i++;
  }
  return list;
}

* This source code was highlighted with Source Code Highlighter.
Теперь заменять различные форы стало чуточку удобнее. Код стал красивее.
Пример использования:
// Проставляем нумерацию элементам в списке.
ddSelector.Items.Cast<ListItem>().Each((x, i) => x.Text = (i+1) + ": " + x.Text);
// Устанавливаем всё в положение выделено
radioButtonList1.Items.Cast<ListItem>().Each(x => x.Selected = true);
// Иногда бывает, что у какого-то объекта есть метод Add но нет метода AddRange.
listToAdd.Each(someObject.Add);
// Так как object.Add сам является функцией, то указания лямбда выражения для него не требуется.

* This source code was highlighted with Source Code Highlighter.
Начиная с некоторого момента я столкнулся с ещё одной проблемой.
Есть длинный ряд методов и Linq, потом какой-то Each, потом снова Linq, потом снова Each, а потом вся коллекция отдаётся чёрт знает куда, а там уже используется только, скажем, первые 10 элементов этой коллекции.
Получается, что всё вычислили, выполнили, присвоили, а результат никому не нужен. Чтобы побороть эту нехорошесть пришлось пополнить список ещё одним Each.
public static IEnumerable<T> EachLazy<T>(this IEnumerable<T> list, Action<T> func)
{     
  foreach (var elem in list)
  {
    func(elem);
    yield return elem;
  }
}

* This source code was highlighted with Source Code Highlighter.
Благодаря конструкции yield return мы получаем то, что действие будет выполнено только тогда, когда нам действительно понадобятся значения.
Соответственно появилась возможность писать конструкции
return ArrayOfInstances.EachLazy(x => x.Parametr = defaultValue).Select(x => x.SomeValue)
   .EachLazy(x => x.Parent = null);

Вот такой-вот рукотворный сахар.
Tags:
Hubs:
+14
Comments57

Articles