Каков наилучший способ перебора строго типизированного универсального списка <T>? - PullRequest
15 голосов
/ 19 августа 2008

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

Ответы [ 7 ]

30 голосов
/ 19 августа 2008

Для C #:

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

Ответ для VB.NET от Фиолетовый Муравей :

For Each objectItem as ObjectType in objectTypeList
    'Do some stuff '
Next
20 голосов
/ 19 августа 2008

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

4 голосов
/ 19 августа 2008

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

4 голосов
/ 19 августа 2008

для VB.NET:

<code>For Each tmpObject as ObjectType in ObjectTypeList
    'Do some stuff '
Next
2 голосов
/ 19 августа 2008

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

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

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

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

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

1 голос
/ 19 августа 2008

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

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

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

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

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