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

StackOverflow https://stackoverflow.com/questions/15204

Вопрос

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

Это было полезно?

Решение

Для C#:

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

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

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

Другие советы

При любой универсальной реализации 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();

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

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

Для 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))

Как указал Grauenwolf, приведенный выше 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.

Есть ли в этом смысл?

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

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

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

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

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

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

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top