Самый элегантный способ обработки первого элемента IEnumerable по-другому - PullRequest
7 голосов
/ 01 июля 2011

Что может быть самым элегантным способом обработки первого элемента 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 ]

12 голосов
/ 01 июля 2011

Если бы мне нужно было сделать это в нескольких местах, я бы извлек метод:

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);

Есть и другие варианты, но это действительно будет зависеть от ситуации.

1 голос
/ 01 июля 2011

Вот еще один способ:

    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

РЕДАКТИРОВАТЬ: "Голова" и "Хвост", вероятно, были бы лучше термины, но я слишком ленив, чтобы пойти изменить его сейчас.

0 голосов
/ 01 июля 2011

Вы можете сделать это по старинке:

var itemsList = items.ToList();
ProcessDifferently(itemsList[0]);

for(int i=1;i<itemsList.Count;i++)
{
   ProcessNormally(itemsList[i]);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...