Qual é a melhor maneira de iterar por meio de um List<T> genérico fortemente tipado?
-
08-06-2019 - |
Pergunta
Qual é a melhor maneira de iterar por meio de uma lista genérica fortemente tipada em C#.NET e VB.NET?
Solução
Para C#:
foreach(ObjectType objectItem in objectTypeList)
{
// ...do some stuff
}
Resposta para VB.NET de Formiga Roxa:
For Each objectItem as ObjectType in objectTypeList
'Do some stuff '
Next
Outras dicas
Com qualquer implementação genérica de IEnumerable, a melhor maneira é:
//C#
foreach( var item in listVariable) {
//do stuff
}
Há, no entanto, uma exceção importante.IEnumerable envolve uma sobrecarga de Current() e MoveNext() que é onde o loop foreach é realmente compilado.
Quando você tem um array simples de estruturas:
//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
int item = valueTypeArray[i];
//do stuff
}
É mais rápido.
Atualizar
Após uma discussão com @Steven Sudit (ver comentários), acho que meu conselho original pode estar desatualizado ou errado, então fiz alguns testes:
// 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();
Então, executei isso no lançamento com todas as otimizações:
list foreach: 00:00:00.5137506
list for : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for : 00:00:00.0954890
E então depure sem otimizações:
list foreach: 00:00:01.1289015
list for : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for : 00:00:00.4913245
Portanto, parece bastante consistente, for
é mais rápido que foreach
e matrizes são mais rápidas que listas genéricas.
No entanto, isso ocorre em 100 milhões de iterações e a diferença é de cerca de 0,4 segundo entre os métodos mais rápidos e mais lentos.A menos que você esteja fazendo loops críticos de desempenho massivos, não vale a pena se preocupar.
Para VB.NET:
For Each tmpObject as ObjectType in ObjectTypeList
'Do some stuff '
Next
C#
myList<string>().ForEach(
delegate(string name)
{
Console.WriteLine(name);
});
Delegados anônimos não estão atualmente implementados em VB.Net, mas tanto C# quanto VB.Net devem ser capazes de fazer lambdas:
C#
myList<string>().ForEach(name => Console.WriteLine(name));
VB.Net
myList(Of String)().ForEach(Function(name) Console.WriteLine(name))
Como Grauenwolf apontou, o VB acima não será compilado, pois o lambda não retorna um valor.Um loop ForEach normal, como outros sugeriram, é provavelmente o mais fácil por enquanto, mas, como sempre, é necessário um bloco de código para fazer o que o C# pode fazer em uma linha.
Aqui está um exemplo banal de por que isso pode ser útil:isso lhe dá a capacidade de passar a lógica do loop de outro escopo que não onde o IEnumerable existe, então você nem precisa expô-lo se não quiser.
Digamos que você tenha uma lista de caminhos de URL relativos que deseja tornar absolutos:
public IEnumerable<String> Paths(Func<String> formatter) {
List<String> paths = new List<String>()
{
"/about", "/contact", "/services"
};
return paths.ForEach(formatter);
}
Então você poderia chamar a função desta forma:
var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);
Dando-lhe "http://myhost.com/about", "http://myhost.com/contact"
etc.Obviamente existem maneiras melhores de fazer isso neste exemplo específico, estou apenas tentando demonstrar o princípio básico.
Sem conhecer a implementação interna de uma lista, acho que geralmente a melhor maneira de iterá-la seria um loop foreach.Como foreach usa um IEnumerator para percorrer a lista, cabe à própria lista determinar como passar de um objeto para outro.
Se a implementação interna fosse, digamos, uma lista vinculada, então um loop for simples seria um pouco mais lento que um foreach.
Isso faz sentido?
Depende da sua aplicação:
- for loop, se a eficiência for uma prioridade
- loop foreach ou método ForEach, o que comunicar sua intenção com mais clareza
Posso estar faltando alguma coisa, mas iterar uma lista genérica deve ser bastante simples se você usar meus exemplos abaixo.A classe List<> implementa as interfaces IList e IEnumerable para que você possa iterá-las facilmente da maneira que desejar.
A maneira mais eficiente seria usar um loop for:
for(int i = 0; i < genericList.Count; ++i)
{
// Loop body
}
Você também pode optar por usar um loop foreach:
foreach(<insertTypeHere> o in genericList)
{
// Loop body
}