¿Cuál es la mejor manera de iterar a través de una Lista <T> genérica fuertemente tipada?

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

Pregunta

¿Cuál es la mejor manera de iterar a través de una lista genérica fuertemente tipada en C#.NET y VB.NET?

¿Fue útil?

Solución

Para C#:

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

Respuesta para VB.NET de hormiga morada:

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

Otros consejos

Con cualquier implementación genérica de IEnumerable, la mejor manera es:

//C#
foreach( var item in listVariable) {
    //do stuff
}

Sin embargo, hay una excepción importante.IEnumerable implica una sobrecarga de Current() y MoveNext(), que es en lo que realmente se compila el bucle foreach.

Cuando tienes una matriz simple de estructuras:

//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
     int item = valueTypeArray[i];
     //do stuff
}

Es más rápido.


Actualizar

Después de una discusión con @Steven Sudit (ver comentarios), creo que mi consejo original puede estar desactualizado o ser incorrecto, así que realicé algunas pruebas:

// 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();

Entonces, ejecuté esto en el lanzamiento con todas las optimizaciones:

list  foreach: 00:00:00.5137506
list  for    : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for    : 00:00:00.0954890

Y luego depurar sin optimizaciones:

list  foreach: 00:00:01.1289015
list  for    : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for    : 00:00:00.4913245

Entonces parece bastante consistente, for es más rápido que foreach y las matrices son más rápidas que las listas genéricas.

Sin embargo, esto abarca 100.000.000 de iteraciones y la diferencia es de aproximadamente 0,4 segundos entre los métodos más rápidos y más lentos.A menos que esté realizando ciclos masivos de rendimiento crítico, no vale la pena preocuparse.

Para VB.NET:

For Each tmpObject as ObjectType in ObjectTypeList
    'Do some stuff '
Next

C#

myList<string>().ForEach(
    delegate(string name)
    {
        Console.WriteLine(name);
    });

Los delegados anónimos no están implementados actualmente en VB.Net, pero tanto C# como VB.Net deberían poder hacer lambdas:

C#

myList<string>().ForEach(name => Console.WriteLine(name));

VB.Net

myList(Of String)().ForEach(Function(name) Console.WriteLine(name))

Como señaló Grauenwolf, el VB anterior no se compilará ya que lambda no devuelve un valor.Un bucle ForEach normal, como otros han sugerido, es probablemente el más fácil por ahora, pero como siempre, se necesita un bloque de código para hacer lo que C# puede hacer en una línea.


Aquí hay un ejemplo trillado de por qué esto podría ser útil:esto le brinda la capacidad de pasar la lógica del bucle desde otro ámbito distinto al que existe IEnumerable, por lo que ni siquiera tiene que exponerlo si no lo desea.

Supongamos que tiene una lista de rutas de URL relativas que desea convertir en absolutas:

public IEnumerable<String> Paths(Func<String> formatter) {
    List<String> paths = new List<String>()
    {
        "/about", "/contact", "/services"
    };

    return paths.ForEach(formatter);
}

Entonces podrías llamar a la función de esta manera:

var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);

Dandote "http://myhost.com/about", "http://myhost.com/contact" etc.Obviamente, hay mejores formas de lograr esto en este ejemplo específico; solo intento demostrar el principio básico.

Sin conocer la implementación interna de una lista, creo que, en general, la mejor manera de iterar sobre ella sería un bucle foreach.Debido a que foreach usa un IEnumerator para recorrer la lista, depende de la lista misma determinar cómo pasar de un objeto a otro.

Si la implementación interna fuera, digamos, una lista enlazada, entonces un bucle for simple sería bastante más lento que un foreach.

¿Tiene sentido?

Depende de su aplicación:

  • bucle for, si la eficiencia es una prioridad
  • bucle foreach o método ForEach, el que comunique su intención con mayor claridad

Puede que me esté perdiendo algo, pero recorrer una lista genérica debería ser bastante sencillo si utiliza mis ejemplos a continuación.La clase List<> implementa las interfaces IList e IEnumerable para que pueda iterar fácilmente a través de ellas básicamente de cualquier forma que desee.

La forma más eficaz sería utilizar un bucle for:

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

También puedes optar por utilizar un bucle foreach:

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top