Каков наилучший способ итерации через строго типизированный общий список <T>?

Каков наилучший способ перебора строго типизированного списка в С#.NET и VB.NET?

Ответы

Ответ 1

Для С#:

foreach(ObjectType objectItem in objectTypeList)
{
    // ...do some stuff
}

Ответ для VB.NET от Фиолетовый Ant:

For Each objectItem as ObjectType in objectTypeList
    'Do some stuff '
Next

Ответ 2

При любой общей реализации IEnumerable лучший способ:

//C#
foreach( var item in listVariable) {
    //do stuff
}

Однако существует важное исключение. IEnumerable включает в себя накладные расходы Current() и MoveNext(), которые фактически скомпилированы в цикле foreach.

Если у вас есть простой массив структур:

//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
     int item = valueTypeArray[i];
     //do stuff
}

Быстрее.


Обновление

После обсуждения с @Steven Sudit (см. комментарии), я думаю, что мой первоначальный совет может быть устаревшим или ошибочным, поэтому я провел несколько тестов:

// create a list to test with
var theList = Enumerable.Range(0, 100000000).ToList();

// time foreach
var sw = Stopwatch.StartNew();
foreach (var item in theList)
{
    int inLoop = item;
}
Console.WriteLine("list  foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
int cnt = theList.Count;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theList[i];
}
Console.WriteLine("list  for    : " + sw.Elapsed.ToString());

// now run the same tests, but with an array
var theArray = theList.ToArray();

sw.Reset();
sw.Start();

foreach (var item in theArray)
{
    int inLoop = item;
}
Console.WriteLine("array foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
cnt = theArray.Length;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theArray[i];
}
Console.WriteLine("array for    : " + sw.Elapsed.ToString());

Console.ReadKey();

Итак, я запускал это в выпуске со всеми оптимизациями:

list  foreach: 00:00:00.5137506
list  for    : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for    : 00:00:00.0954890

И затем отлаживать без оптимизаций:

list  foreach: 00:00:01.1289015
list  for    : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for    : 00:00:00.4913245

Таким образом, он выглядит достаточно согласованным, for быстрее, чем foreach, а массивы быстрее, чем общие списки.

Однако это более 100 000 000 итераций, и разница составляет около 0,4 секунды между самыми быстрыми и самыми медленными методами. Если вы не делаете массивные критические кривые производительности, просто не стоит беспокоиться.

Ответ 3

Для VB.NET:

For Each tmpObject as ObjectType in ObjectTypeList
    'Do some stuff '
Next

Ответ 4

С#

myList<string>().ForEach(
    delegate(string name)
    {
        Console.WriteLine(name);
    });

Анонимные делегаты в настоящее время не реализованы в VB.Net, но оба С# и VB.Net должны иметь возможность делать lambdas:

С#

myList<string>().ForEach(name => Console.WriteLine(name));

VB.Net

myList(Of String)().ForEach(Function(name) Console.WriteLine(name))

Как отметил Грауэнволф, вышеупомянутый VB не будет компилироваться, поскольку лямбда не возвращает значение. Обычный цикл ForEach, как и другие, предположил, вероятно, самый простой на данный момент, но, как обычно, блок блок выполняет то, что С# может делать в одной строке.


Вот банальный пример того, почему это может быть полезно: это дает вам возможность передавать логику цикла из другой области, кроме IEnumerable, поэтому вам даже не нужно ее раскрывать, если вы не хотите к.

Скажите, что у вас есть список относительных путей URL, которые вы хотите сделать абсолютными:

public IEnumerable<String> Paths(Func<String> formatter) {
    List<String> paths = new List<String>()
    {
        "/about", "/contact", "/services"
    };

    return paths.ForEach(formatter);
}

Итак, вы можете вызвать функцию следующим образом:

var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);

Предоставление вам "http://myhost.com/about", "http://myhost.com/contact" и т.д. Очевидно, что есть лучшие способы выполнить это в этом конкретном примере, я просто пытаюсь продемонстрировать основной принцип.

Ответ 5

Не зная внутренней реализации списка, я думаю, что лучший способ перебора по ней - это цикл foreach. Поскольку foreach использует IEnumerator для переходов по списку, это зависит от самого списка, чтобы определить, как перемещаться из объекта в объект.

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

Это имеет смысл?

Ответ 6

Это зависит от вашего приложения:

  • для цикла, если эффективность является приоритетом
  • цикл foreach или метод ForEach, в зависимости от того, что более четко связывает ваши намерения.

Ответ 7

Мне может быть что-то не хватает, но повторение через общий список должно быть довольно простым, если вы используете мои примеры ниже. Класс List < > реализует интерфейсы IList и IEnumerable, так что вы можете легко перебирать их в основном любым способом, который вы хотите.

Наиболее эффективным способом было бы использовать цикл for:

for(int i = 0; i < genericList.Count; ++i) 
{
     // Loop body
}

Вы также можете использовать цикл foreach:

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}