Самый элегантный способ обработки первого элемента IEnumerable по-другому
Что может быть самым элегантным способом обработки первого элемента IEnumerable иначе, чем другие, без необходимости проверять каждую итерацию?
С тестом на каждой итерации это будет выглядеть так:
// "first item done" flag
bool firstDone = false;
// items is an IEnumerable<something>
foreach (var item in items)
{
if (!firstDone)
{
// do this only once
ProcessDifferently(item);
firstDone = true;
continue;
}
ProcessNormally(item);
}
Если я сделаю это:
ProcessDifferently(items.First());
ProcessNormally(items.Skip(1)); // this calls `items.GetEnumerator` again
это вызовет GetEnumerator
дважды, чего я бы хотел избежать (например, для случаев Linq-Sql).
Как бы вы это сделали, если вам нужно сделать несколько раз вокруг вашего кода?
3 ответа
Решение
Если бы мне нужно было сделать это в нескольких местах, я бы извлек метод:
public void Process<T>(IEnumerable<T> source,
Action<T> firstAction,
Action<T> remainderAction)
{
// TODO: Argument validation
using (var iterator = source.GetEnumerator())
{
if (iterator.MoveNext())
{
firstAction(iterator.Current);
}
while (iterator.MoveNext())
{
remainderAction(iterator.Current);
}
}
}
Называется как:
Process(items, ProcessDifferently, ProcessNormally);
Есть и другие варианты, но это будет зависеть от ситуации.
Вот еще один способ:
private static void Main(string[] args)
{
var testdata = new[] { "a", "b", "c", "d", "e" };
var action = FirstThenRest<string>(
s => Console.WriteLine("First: " + s),
s => Console.WriteLine("Rest: " + s));
foreach (var s in testdata)
action(s);
}
public static Action<T> FirstThenRest<T>(Action<T> first, Action<T> rest)
{
Action<T> closure = t =>
{
first(t);
closure = rest;
};
return t => closure(t);
}
Это выводит:
First: a
Rest: b
Rest: c
Rest: d
Rest: e
Без условий.:D
РЕДАКТИРОВАТЬ: "Голова" и "Хвост", вероятно, было бы лучше термины, но я слишком ленив, чтобы пойти изменить это сейчас.
Вы можете сделать это по старинке:
var itemsList = items.ToList();
ProcessDifferently(itemsList[0]);
for(int i=1;i<itemsList.Count;i++)
{
ProcessNormally(itemsList[i]);
}