处理第一次通过foreach的简单方法?

时间:2010-02-21 23:39:11

标签: c# foreach

我经常发现自己在foreach循环中执行以下索引计数器混乱,以查明我是否在第一个元素。是否有更优雅的方式在 C#中执行此操作,类似于if(this.foreach.Pass == 1)等。

int index = 0;
foreach (var websitePage in websitePages) {
    if(index == 0)
        classAttributePart = " class=\"first\"";
    sb.AppendLine(String.Format("<li" + classAttributePart + ">" + 
        "<a href=\"{0}\">{1}</a></li>", 
        websitePage.GetFileName(), websitePage.Title));
    index++;
}

12 个答案:

答案 0 :(得分:13)

另一种方法是接受“丑陋的部分”必须在某处实现并提供隐藏“丑陋部分”的抽象,这样你就不必在多个地方重复它并且可以专注于特定的算法。这可以使用C#lambda表达式完成(或者如果你只限于.NET 2.0,则使用C#2.0匿名委托):

void ForEachWithFirst<T>(IEnumerable<T> en, 
     Action<T> firstRun, Action<T> nextRun) {
  bool first = true;
  foreach(var e in en) {
    if (first) { first = false; firstRun(e); } else nextRun(e);
  }
}

现在您可以使用此可重用方法来实现您的算法:

ForEachWithFirst(websitePages,
  (wp => sb.AppendLine(String.Format("<li class=\"first\">" +
         "<a href=\"{0}\">{1}</a></li>", wp.GetFileName(), wp.Title)))
  (wp => sb.AppendLine(String.Format("<li>" + 
         "<a href=\"{0}\">{1}</a></li>", wp.GetFileName(), wp.Title))) );

您可以根据确切的重复模式设计不同的抽象。好处是 - 由于lambda表达式 - 抽象的结构完全取决于你。

答案 1 :(得分:11)

稍微不那么冗长:

string classAttributePart = " class=\"first\"";
foreach (var websitePage in websitePages)
{
    sb.AppendLine(String.Format("<li" + classAttributePart + "><a href=\"{0}\">{1}</a></li>", websitePage.GetFileName(), websitePage.Title));
    classAttributePart = string.Empty;
}

如果您使用的是.NET 3.5,则可以使用Select的重载来为您提供索引和测试。那么你也不需要StringBuilder。这是代码:

string[] s = websitePages.Select((websitePage, i) =>
        String.Format("<li{0}><a href=\"{1}\">{2}</a></li>\n",
                      i == 0 ? " class=\"first\"" : "",
                      websitePage.GetFileName(),
                      websitePage.Title)).ToArray();

string result = string.Join("", s);

它看起来有点冗长,但这主要是因为我打破了许多较短的长线。

答案 2 :(得分:4)

if (websitePages.IndexOf(websitePage) == 0)
    classAttributePart = " class=\"last\"";

这可能更优雅,但它可能性能较差,因为它必须检查每个元素的索引。

答案 3 :(得分:4)

这可能略微更好

bool doInit = true;
foreach (var websitePage in websitePages)
{
    if (doInit)
    {
        classAttributePart = " class=\"first\"";
        doInit = false;
    }
    sb.AppendLine(String.Format("<li" + classAttributePart + "><a href=\"{0}\">{1}</a></li>", websitePage.GetFileName(), websitePage.Title));
}

我最终也做了很多这样的事情,也让我感到烦恼。

答案 4 :(得分:4)

您可以使用for循环而不是foreach循环。在这种情况下,你的for循环可以将它的索引从1开始,如果长度大于0,你可以在循环之外做第一个元素。

至少在这种情况下,你不会在每次迭代时进行额外的比较。

答案 5 :(得分:3)

另一种方法是使用jQuery's first selector来设置类而不是服务器端代码。

$(document).ready(function(){ 
     $("#yourListId li:first").addClass("first");
}

答案 6 :(得分:2)

如果你只为第一个索引做这个,你可以在foreach循环之前完成。

答案 7 :(得分:2)

如果您只对第一个元素感兴趣,那么最好(最可读)的方法是使用LINQ来找出第一个元素。像这样:

var first = collection.First();
// do something with first element
....
foreach(var item in collection){
    // do whatever you need with every element
    ....
    if(item==first){
        // and you can still do special processing here provided there are no duplicates
    }
}

如果您需要索引的数值或非第一索引,您可以随时

foreach (var pair in collection.Select((item,index)=>new{item,index}))
{
    // do whatever you need with every element
    ....
    if (pair.index == 5)
    {
        // special processing for 5-th element. If you need to do this, your design is bad bad bad
    }
}

PS最好的方法是使用for - 循环。仅在foreach不可用时使用for(即集合为IEnumerable而不是列表或其他内容)

答案 8 :(得分:1)

这个怎么样?

var iter = websitePages.GetEnumerator();
iter.MoveNext();
//Do stuff with the first element
do {
    var websitePage = iter.Current;
    //For each element (including the first)...
} while (iter.MoveNext());

答案 9 :(得分:0)

正如我父亲所说,“使用合适的工具来完成工作”。

在这种情况下,看看你的循环需要做什么。

  • 如果它是可以在循环外完成的东西,那么移动它就可以使用foreach了。
  • 如果您需要处理复杂的案例组合,那么您可能需要考虑使用不同的模式(例如:状态模式,或任何最合适的模式)来处理您想要在循环内部执行的操作,在这种情况下你选择了当时最有意义的循环结构。
  • 如果你的循环依赖于能够提取迭代索引以将其传递到别处,那么也许for循环可能是更好的选择。

否则,为了回答你的问题,似乎没有一种简单的非冗长的方法来识别列表项的索引,因为它是在foreach循环中访问的。

答案 10 :(得分:0)

public static class ExtenstionMethods
{
    public static IEnumerable<KeyValuePair<Int32, T>> Indexed<T>(this IEnumerable<T> collection)
    {
        Int32 index = 0;

        foreach (var value in collection)
        {
            yield return new KeyValuePair<Int32, T>(index, value);
            ++index;
        }
    }
}

foreach (var iter in websitePages.Indexed())
{
    var websitePage = iter.Value;
    if(iter.Key == 0) classAttributePart = " class=\"first\"";
    sb.AppendLine(String.Format("<li" + classAttributePart + "><a href=\"{0}\">{1}</a></li>", websitePage.GetFileName(), websitePage.Title));
}

答案 11 :(得分:0)

在这个例子中,你可以通过这种方式去掉索引。

foreach (var websitePage in websitePages) 
{ 
    classAttributePart =  classAttributePart ?? " class=\"first\""; 
    sb.AppendLine(String.Format("<li" + classAttributePart + "><a href=\"{0}\">{1}</a></li>", websitePage.GetFileName(), websitePage.Title)); 
} 

最好检查结果数据变量以执行此类任务。 在这种情况下,它检查classAttributePart字符串的null并附加初始值。