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

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

Ответов (7)

Решение

Для C#:

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

Ответ для VB.NET от Purple Ant :

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

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

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

Для VB.NET:

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

C#

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

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

C#

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

VB.Net

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

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


Вот банальный пример того, почему это может быть полезно: это дает вам возможность передавать логику цикла из другой области, отличной от той, в которой существует 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" и т. Д. Очевидно, что в этом конкретном примере есть более эффективные способы сделать это, я просто пытаюсь продемонстрировать основной принцип.

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

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

Имеет ли это смысл?

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

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

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

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

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

С любой общей реализацией 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 массивы, и быстрее, чем общие списки.

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