Подтвердить что ты не робот

Менее сложный способ обработки первого прохода через foreach?

Я часто обнаруживаю, что в цикле foreach я делаю следующее индекс-счетчик беспорядок, чтобы узнать, есть ли я в первом элементе или нет. Есть ли более элегантный способ сделать это в С#, что-то вроде строк 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++;
}
4b9b3361

Ответ 1

Другой подход заключается в том, чтобы признать, что "уродливая часть" должна быть реализована где-то и обеспечить абстракцию, которая скрывает "уродливую часть", так что вам не нужно повторять ее в нескольких местах и ​​может сфокусироваться на конкретном алгоритме, Это можно сделать с помощью выражений лямбда С# (или с помощью анонимных делегатов С# 2.0, если вы ограничены .NET.NET):

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

Вы можете проектировать абстракцию по-разному в зависимости от точного повторяющегося шаблона. Хорошо, что благодаря лямбда-выражению - структура абстракции полностью зависит от вас.

Ответ 2

Немного менее подробный:

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

Это выглядит немного более многословным, но в основном потому, что я сломал очень длинную строку на несколько более коротких.

Ответ 3

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

Это может быть более элегантно, но, вероятно, будет менее результативным, поскольку он должен проверять индекс каждого элемента.

Ответ 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));
}

В конечном итоге я тоже очень много делаю, и это тоже меня тоже беспокоит.

Ответ 5

Вы можете использовать цикл for вместо цикла foreach. В этом случае цикл for может начинать его с индекса 1, и вы можете сделать первый элемент вне цикла, если длина больше 0.

По крайней мере, в этом случае вы не будете делать дополнительное сравнение на каждой итерации.

Ответ 6

Другим подходом было бы использовать jQuery first selector, чтобы установить класс вместо кода на стороне сервера.

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

Ответ 7

Вы можете сделать это до цикла foreach, если вы делаете это только для первого индекса.

Ответ 8

Если вам интересен только первый элемент, лучшим (как в большинстве читаемых) способом является использование 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 -loop. Используйте foreach, только если for недоступен (т.е. Коллекция IEnumerable, а не список или что-то еще)

Ответ 9

Как насчет этого?

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

Ответ 10

Как мой папа любит говорить "Использовать правильный инструмент для работы".

В этом случае, посмотрите, что нужно сделать вашему циклу.

  • Если это что-то, что можно сделать за пределами цикла, переместите его, и ваше использование foreach в порядке.
  • Если вам нужно обработать сложную комбинацию случаев, тогда вам может потребоваться использовать другой шаблон (например: шаблон состояния или что-то самое подходящее) для материала, который вы хотите сделать внутри цикла, и в этом случае вы выбираете конструкцию цикла, которая имеет наибольший смысл в то время.
  • Если ваш цикл зависит от возможности извлечь индекс итерации, чтобы передать его в другом месте, возможно, лучший цикл может быть в цикле for.

В противном случае, чтобы ответить на ваш вопрос, похоже, нет легкого не-многословного средства для идентификации индекса элемента списка, поскольку он обращается в цикле foreach.

Ответ 11

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

Ответ 12

В этом примере вы можете избавиться от проверки индекса таким образом.

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

Было бы лучше проверить результирующую переменную данных для выполнения этой задачи. В этом случае он проверяет значение null в строке classAttributePart и добавляет начальное значение.