【问题标题】:Most elegant way to process first IEnumerable item differently以不同方式处理第一个 IEnumerable 项目的最优雅方式
【发布时间】:2011-07-01 16:44:55
【问题描述】:

以不同方式处理第一个 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-to-Sql 的情况)。

如果你需要围绕你的代码做几次,你会怎么做?

【问题讨论】:

    标签: linq ienumerable skip


    【解决方案1】:

    如果我需要在几个地方做,我会提取一个方法:

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

    还有其他的选择,不过要视情况而定。

    【讨论】:

    • 谢谢,干净整洁。完全符合我的要求。
    【解决方案2】:

    这是另一种方式:

        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

    编辑:“头”和“尾”可能是更好的术语,但我懒得现在去改变它。

    【讨论】:

      【解决方案3】:

      你可以用老式的方式来做:

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

      【讨论】:

      • 这适用于列表和数组,但我需要为 IEnumerable 做同样的事情,而不需要重复两次。
      • 啊,是的,你是对的。为了清楚起见,我修改了代码示例。用飞碟回答 - 非常好。
      猜你喜欢
      • 1970-01-01
      • 2013-04-23
      • 2015-04-10
      • 1970-01-01
      • 2011-02-20
      • 2016-07-12
      • 2010-10-12
      • 1970-01-01
      • 2013-03-26
      相关资源
      最近更新 更多