Каков наилучший способ перебора строго типизированного универсального списка в C # .NET и VB.NET?
Для C #:
foreach(ObjectType objectItem in objectTypeList)
{
// ...do some stuff
}
Ответ для VB.NET от Purple Ant :
For Each objectItem as ObjectType in objectTypeList
'Do some stuff '
Next
Возможно, я что-то упускаю, но итерация общего списка должна быть довольно простой, если вы используете мои примеры ниже. Класс List <> реализует интерфейсы IList и IEnumerable, так что вы можете легко перебирать их практически любым способом.
Наиболее эффективным способом было бы использовать цикл for:
for(int i = 0; i < genericList.Count; ++i)
{
// Loop body
}
Вы также можете использовать цикл foreach:
foreach(<insertTypeHere> o in genericList)
{
// Loop body
}
<insertTypeHere>
. Компилятор сделает это за вас var
. Для VB.NET:
For Each tmpObject as ObjectType in ObjectTypeList
'Do some stuff '
Next
Не зная внутренней реализации списка, я думаю, что в общем случае наилучшим способом его перебора будет цикл foreach. Поскольку foreach использует IEnumerator для обхода списка, сам список определяет, как перемещаться от объекта к объекту.
Если бы внутренняя реализация была, скажем, связанным списком, то простой цикл for был бы немного медленнее, чем foreach.
Имеет ли это смысл?
Для любой общей реализации 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 секунды. Если вы не делаете огромные циклы, критичные к производительности, об этом не стоит беспокоиться.
List<T>
, а не массивах. Несмотря на это, я не верю, что foreach
в нативном массиве фактически используется IEnumerable
, по крайней мере, не в оптимизированном коде, поэтому реального ускорения не будет. Фактически, мои тестовые шоу foreach
занимают всего три четверти времени for
. foreach
циклы действительно страдают от for
циклов после применения оптимизации компилятора. Однако я немного покопался - в случае с массивом foreach
оптимизирован точно такой же IL, как и у for
. Для List<T>
компилятора не может использовать эту оптимизацию , и полученный IL немного медленнее. Похоже, мы оба не правы (._.)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 или метод ForEach, в зависимости от того, что ваше намерение яснее
foreach(var item in itemlist)
.