编写循环的简洁方法,该循环具有集合中第一个项目的特殊逻辑

时间:2010-12-08 17:10:48

标签: c# collections

通常我必须编写一个循环,该循环必须特殊情况下集合中的第一个项目,代码似乎永远不会像它应该的那样清晰。

如果不重新设计C#语言,那么编码这些循环的最佳方法是什么?

// this is more code to read then I would like for such a common concept
// and it is to easy to forget to update "firstItem"
foreach(x in yyy)
{
  if (firstItem)
  {
     firstItem = false;
     // other code when first item
  }
  // normal processing code
}

// this code is even harder to understand
if (yyy.Length > 0)
{
   //Process first item;
   For(int I = 1; I <yyy.Length; i++)
   {  
      // process the other items.
   }
}

12 个答案:

答案 0 :(得分:13)

怎么样:

using (var erator = enumerable.GetEnumerator())
{
    if (erator.MoveNext())
    {
        ProcessFirst(erator.Current);
        //ProcessOther(erator.Current); // Include if appropriate.

        while (erator.MoveNext())
            ProcessOther(erator.Current);
    }
}

如果您愿意,可以将其转换为扩展程序:

public static void Do<T>(this IEnumerable<T> source, 
                         Action<T> firstItemAction,
                         Action<T> otherItemAction)
{
   // null-checks omitted

    using (var erator = source.GetEnumerator())
    {
        if (!erator.MoveNext())
            return;

        firstItemAction(erator.Current);

        while (erator.MoveNext())
           otherItemAction(erator.Current);            
    }
}

答案 1 :(得分:5)

我很想使用一些linq

using System.Linq;

var theCollectionImWorkingOn = ...

var firstItem = theCollectionImWorkingOn.First();
firstItem.DoSomeWork();

foreach(var item in theCollectionImWorkingOn.Skip(1))
{
    item.DoSomeOtherWork();
}

答案 2 :(得分:5)

你可以尝试:

collection.first(x=>
{
    //...
}).rest(x=>
{
    //...
}).run();

第一个/休息看起来像:

FirstPart<T> first<T>(this IEnumerable<T> c, Action<T> a)
{
    return new FirstPart<T>(c, a);
}

FirstRest rest<T>(this FirstPart<T> fp, Action<T> a)
{
    return new FirstRest(fp.Collection, fp.Action, a);
}

您需要定义分类的FirstPart和FirstRest。 FirstRest需要像这样的run方法(Collection,FirstAction和RestAction属性):

void run()
{
    bool first = true;
    foreach (var x in Collection)
    {
        if (first) {
            FirstAction(x);
            first = false;
        }
        else {
             RestAction(x);
        }
    }
}

答案 3 :(得分:4)

我一直使用first变量方法,这对我来说似乎完全正常。 如果您更喜欢这样,可以使用LINQ First()Skip(1)

var firstItem = yyy.First();
// do the whatever on first item

foreach (var y in yyy.Skip(1))
{
// process the rest of the collection
}

答案 4 :(得分:2)

你写它的方式可能是最简洁的写法。毕竟,存在特定于第一个元素的逻辑,因此必须以某种方式表示。

答案 5 :(得分:2)

恕我直言最干净的方法是:尽量避免第一项的特殊情况。当然,这可能不适用于所有情况,但“特殊情况”可能表明您的程序逻辑比它需要的更复杂。

顺便说一句,我不会编码

if (yyy.Length > 0)
{
   for(int i = 1; i <yyy.Length; i++)
   {  
      // ...
   }
}

但是

   for(int i = 1; i <yyy.Length; i++)
   {  
      // ...
   }

(这本身就是一个如何避免不必要处理特殊情况的简单例子。)

答案 6 :(得分:1)

在这种情况下,我会像这样使用for循环:

for(int i = 0;  i < yyy.Count; i++){
      if(i == 0){
          //special logic here
      }
}

使用for循环还可以让你在其他情况下做一些特别的事情,比如最后一项,甚至是序列中的项目,等等。

答案 7 :(得分:1)

这是一个稍微简单的扩展方法来完成这项工作。这是KeithS's solutionmy answer to a related Java question的组合:

public static void ForEach<T>(this IEnumerable<T> elements,
                              Action<T> firstElementAction,
                              Action<T> standardAction)
{
    var currentAction = firstElementAction;
    foreach(T element in elements)
    {
        currentAction(element);
        currentAction = standardAction;
    }
}

答案 8 :(得分:0)

这两种算法都是完全可以接受的处理第一个元素的算法,并且实际上并没有不同的方法。如果重复这种模式,你可以将它隐藏在ForEach()的重载之后:

public static void ForEach<T>(this IEnumerable<T> elements, Action<T> firstElementAction, Action<T> standardAction)
{
    var firstItem = true;
    foreach(T element in elements)
    {
        if(firstItem)
        {
            firstItem = false;
            firstElementAction(element)
        }
        else
            standardAction(element)
    }
}

...

//usage
yyy.ForEach(t=>(other code when first item), t=>(normal processing code));

Linq让它更清洁一点:

PerformActionOnFirstElement(yyy.FirstOrDefault());
yyy.Skip(1).ForEach(x=>(normal processing code));

答案 9 :(得分:0)

虽然我不会亲自这样做,但还有另一种方法using enumerators,它减轻了条件逻辑的需要。像这样:

void Main()
{
    var numbers = Enumerable.Range(1, 5);
    IEnumerator num = numbers.GetEnumerator();

    num.MoveNext();
    ProcessFirstItem(num.Current); // First item

    while(num.MoveNext()) // Iterate rest
    {
        Console.WriteLine(num.Current);
    }

}

    void ProcessFirstItem(object first)
    {
        Console.WriteLine("First is: " + first);
    }

示例输出将是:

First is: 1
2
3
4
5

答案 10 :(得分:0)

我提出的另一个选择是

enum ItemType
{
  First,
  Last,
  Normal
}

list.Foreach(T item, ItemType itemType) =>
{
   if (itemType == ItemType.First)
   {
   }

   // rest of code
};

编写扩展方法留给读者练习... 还应该使用两个布尔标志“IsFirst”和“IsLast”而不是ItemType枚举,或者ItemType是一个具有“IsFirst”和“IsLast”属性的对象吗?

答案 11 :(得分:0)

我的解决方案:

foreach (var x in yyy.Select((o, i) => new { Object = o, Index = i } )
{
  if (x.Index == 0)
  {
    // First item logic
  }
  else
  {
    // Rest of items
  }
}