Quelle est la meilleure façon de parcourir un List<T> générique fortement typé ?
-
08-06-2019 - |
Question
Quelle est la meilleure façon de parcourir une liste générique fortement typée en C#.NET et VB.NET ?
La solution
Pour C# :
foreach(ObjectType objectItem in objectTypeList)
{
// ...do some stuff
}
Réponse pour VB.NET de Fourmi violette:
For Each objectItem as ObjectType in objectTypeList
'Do some stuff '
Next
Autres conseils
Avec toute implémentation générique de IEnumerable, le meilleur moyen est :
//C#
foreach( var item in listVariable) {
//do stuff
}
Il existe cependant une exception importante.IEnumerable implique une surcharge de Current() et MoveNext() dans laquelle la boucle foreach est réellement compilée.
Lorsque vous disposez d’un simple tableau de structures :
//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
int item = valueTypeArray[i];
//do stuff
}
C'est plus rapide.
Mise à jour
Suite à une discussion avec @Steven Sudit (voir commentaires) je pense que mon conseil initial est peut-être obsolète ou erroné, j'ai donc fait quelques tests :
// 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();
J'ai donc exécuté ceci dans la version avec toutes les optimisations :
list foreach: 00:00:00.5137506
list for : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for : 00:00:00.0954890
Et puis déboguez sans optimisations :
list foreach: 00:00:01.1289015
list for : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for : 00:00:00.4913245
Cela semble donc assez cohérent, for
est plus rapide que foreach
et les tableaux sont plus rapides que les listes génériques.
Cependant, cela s'étend sur 100 000 000 d'itérations et la différence est d'environ 0,4 seconde entre les méthodes les plus rapides et les plus lentes.À moins que vous n'effectuiez des boucles massives critiques en termes de performances, cela ne vaut tout simplement pas la peine de s'inquiéter.
Pour VB.NET :
For Each tmpObject as ObjectType in ObjectTypeList
'Do some stuff '
Next
C#
myList<string>().ForEach(
delegate(string name)
{
Console.WriteLine(name);
});
Les délégués anonymes ne sont actuellement pas implémentés dans VB.Net, mais C# et VB.Net devraient être capables de faire des lambdas :
C#
myList<string>().ForEach(name => Console.WriteLine(name));
VB.Net
myList(Of String)().ForEach(Function(name) Console.WriteLine(name))
Comme Grauenwolf l'a souligné, le VB ci-dessus ne sera pas compilé puisque le lambda ne renvoie pas de valeur.Une boucle ForEach normale, comme d'autres l'ont suggéré, est probablement la plus simple pour le moment, mais comme d'habitude, il faut un bloc de code pour faire ce que C# peut faire en une seule ligne.
Voici un exemple banal de la raison pour laquelle cela pourrait être utile :cela vous donne la possibilité de transmettre la logique de boucle à partir d'une autre portée que celle où existe IEnumerable, de sorte que vous n'avez même pas besoin de l'exposer si vous ne le souhaitez pas.
Supposons que vous ayez une liste de chemins d'URL relatifs que vous souhaitez rendre absolus :
public IEnumerable<String> Paths(Func<String> formatter) {
List<String> paths = new List<String>()
{
"/about", "/contact", "/services"
};
return paths.ForEach(formatter);
}
Vous pouvez donc appeler la fonction de cette façon :
var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);
Te donne "http://myhost.com/about", "http://myhost.com/contact"
etc.Évidemment, il existe de meilleures façons d'y parvenir dans cet exemple spécifique, j'essaie simplement de démontrer le principe de base.
Sans connaître l'implémentation interne d'une liste, je pense que généralement la meilleure façon de la parcourir serait une boucle foreach.Étant donné que foreach utilise un IEnumerator pour parcourir la liste, c'est à la liste elle-même de déterminer comment passer d'un objet à l'autre.
Si l'implémentation interne était, disons, une liste chaînée, alors une simple boucle for serait un peu plus lente qu'une foreach.
Cela a-t-il du sens?
Cela dépend de votre application :
- boucle for, si l'efficacité est une priorité
- boucle foreach ou méthode ForEach, selon celle qui communique le plus clairement votre intention
Il me manque peut-être quelque chose, mais parcourir une liste générique devrait être assez simple si vous utilisez mes exemples ci-dessous.La classe List<> implémente les interfaces IList et IEnumerable afin que vous puissiez facilement les parcourir comme vous le souhaitez.
Le moyen le plus efficace serait d'utiliser une boucle for :
for(int i = 0; i < genericList.Count; ++i)
{
// Loop body
}
Vous pouvez également choisir d'utiliser une boucle foreach :
foreach(<insertTypeHere> o in genericList)
{
// Loop body
}