开发者

Most elegant way to process first IEnumerable item differently

What would be the most elegant way to process the first IEnumerable item differently than others, without having to test on each iteration?

With a test on each iteration, it would look like this:

// "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);
}

If I do this:

ProcessDifferently(items.First());
ProcessNormally(items.Skip(1)); // this calls `items.GetEnumerator` again

it will invoke GetEnumerator twice, which I would like to avoid (for Linq-to-Sql cases, for example).

How would you do it, i开发者_StackOverflow中文版f you need to do several times around your code?


If I needed to do it in several places, I'd extract a method:

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

Called as:

Process(items, ProcessDifferently, ProcessNormally);

There are other options too, but it would really depend on the situation.


Here's another way:

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

This outputs:

First: a
Rest: b
Rest: c
Rest: d
Rest: e

No conditionals. :D

EDIT: "Head" and "Tail" would probably be better terms but I'm too lazy to go change it now.


You can do it the old fashioned way:

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

for(int i=1;i<itemsList.Count;i++)
{
   ProcessNormally(itemsList[i]);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜