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