En .NET, ¿qué ciclo se ejecuta más rápido, 'para' o 'foreach'?


345

En C # / VB.NET / .NET, ¿qué ciclo se ejecuta más rápido foro foreach?

Desde que leí que un forbucle funciona más rápido que un foreachbucle hace mucho tiempo, supuse que era cierto para todas las colecciones, colecciones genéricas, todas las matrices, etc.

Recorrí Google y encontré algunos artículos, pero la mayoría de ellos no son concluyentes (lea los comentarios sobre los artículos) y están abiertos.

Lo ideal sería tener cada escenario en la lista y la mejor solución para el mismo.

Por ejemplo (solo un ejemplo de cómo debería ser):

  1. para iterar una matriz de más de 1000 cadenas, fores mejor queforeach
  2. para iterar sobre IListcadenas (no genéricas): foreaches mejor quefor

Algunas referencias encontradas en la web para lo mismo:

  1. Gran artículo original de Emmanuel Schanzer
  2. CodeProject FOREACH Vs. PARA
  3. Blog: para foreacho no foreach, esa es la pregunta
  4. Foro ASP.NET - NET 1.1 C # forvsforeach

[Editar]

Además del aspecto de legibilidad, estoy realmente interesado en hechos y cifras. Hay aplicaciones en las que importa la última milla de optimización de rendimiento.


3
La diferencia aún existe. Las matrices en particular deberían ser igual de rápidas bajo foreach, pero para todo lo demás, los bucles simples son más rápidos. Por supuesto, la mayoría de las veces, esto no hará una diferencia, y por supuesto, un inteligente compilador JIT podría, en teoría, eliminar la diferencia.
jalf

3
Sin contexto, no puedo saber exactamente lo que está haciendo, pero ¿qué sucede cuando se encuentra con una matriz parcialmente llena?
Chris Cudmore

66
Por cierto, 2 millones de visitas / mes no es nada aterrador. Es menos de un golpe por segundo en promedio.
Mehrdad Afshari

37
Nota importante : Esta pregunta se fusionó ayer con una pregunta totalmente no relacionada sobre ser obligado a usar en foreachlugar de foren C #. Si ve aquí respuestas que no tienen ningún sentido, es por eso. Culpe al moderador, no a las respuestas desafortunadas.
TED

77
@TED ​​Oh, me preguntaba de dónde vienen todos los comentarios de "tu jefe es un idiota", gracias
Gaspa79

Respuestas:


350

Patrick Smacchia escribió en su blog sobre este último mes, con las siguientes conclusiones:

  • Los bucles for List son un poco más de 2 veces más baratos que los bucles foreach en List.
  • El bucle en la matriz es aproximadamente 2 veces más barato que el bucle en Lista
  • Como consecuencia, el bucle en la matriz usando for es 5 veces más barato que el bucle en la lista usando foreach (que creo que es lo que todos hacemos).

130
Sin embargo, nunca olvide: "La optimización prematura es la raíz de todo mal".
Oorang

18
@Hardwareguy: una vez que sepa que for es casi imperceptiblemente más rápido, ¿por qué no debería comenzar a usarlo en general? No toma tiempo extra.
DevinB

47
@devinb, usar "for" es más difícil que usar "foreach" ya que agrega código, otra variable, una condición que necesita verificar, etc. ¿Cuántas veces ha visto un error "off-by-one" en un bucle "foreach"? ?
tster

35
@Hardwareguy, déjame ver si entendí esto bien. Se necesita 5 veces más tiempo para recorrer una lista con lo foreachque se necesita para recorrer una matriz for, ¿y lo llama insignificante? Ese tipo de diferencia de rendimiento puede ser importante para su aplicación, y puede que no, pero no lo descartaría sin más.
Robert Harvey

44
Al leer la publicación del blog, parece que las pruebas se ejecutaron en Debug y no Release, por lo que podría tener un factor. Además, la diferencia es específicamente solo para la sobrecarga del bucle. No afecta en absoluto el tiempo para ejecutar el cuerpo del bucle, la mayoría de los casos es mucho más largo que el tiempo que lleva pasar al siguiente elemento de la lista. Es buena información saber cuándo ha identificado que claramente hay un problema y ha medido la diferencia en su aplicación específicamente y hay una mejora notable, pero definitivamente no es un consejo general para eliminar todo foreach.
Davy8

164

Primero, una contrademanda a la respuesta de Dmitry (ahora eliminada) . Para las matrices, el compilador de C # emite en gran medida el mismo código foreachque para un forbucle equivalente . Eso explica por qué para este punto de referencia, los resultados son básicamente los mismos:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

Resultados:

For loop: 16638
Foreach loop: 16529

Luego, valide que el punto de Greg sobre el tipo de colección es importante: cambie la matriz a a List<double>en lo anterior y obtendrá resultados radicalmente diferentes. No solo es significativamente más lento en general, sino que foreach se vuelve significativamente más lento que acceder por índice. Dicho esto, todavía preferiría casi siempre foreach a un bucle for donde simplifica el código, porque la legibilidad es casi siempre importante, mientras que la micro-optimización rara vez lo es.


"cambie la matriz a una Lista <double> en lo anterior, y obtendrá resultados radicalmente diferentes" Muy interesante, no había pensado en eso
johnc

55
Teniendo en cuenta las extrañas diferencias en los resultados entre mis pruebas y parámetros de referencia de otras personas, creo que esto va a merecer una entrada de blog ...
Jon Skeet

1
¿Cuál prefieres casi siempre entre matrices y List<T>? ¿La legibilidad también triunfa sobre la microoptimización en ese caso?
JohnB

12
@JohnB: Sí, casi siempre prefiero las List<T>matrices. Las excepciones son char[]y byte[]que con mayor frecuencia se tratan como "fragmentos" de datos en lugar de colecciones normales.
Jon Skeet

Increíble, obtengo una diferencia aún más agresiva en mi máquina, casi un 10% a favor de foreach en matrices simples. Supongo salvajemente que todo esto se debe a la inquietud de no tener que preocuparse por la variable adicional, los controles encuadernados, etc. Sería muy interesante si alguien tiene una explicación profunda de esto.
Tamir Daniely

162

foreachlos bucles demuestran una intención más específica que los forbucles .

El uso de un foreachbucle demuestra a cualquiera que use su código que planea hacer algo a cada miembro de una colección, independientemente de su lugar en la colección. También muestra que no está modificando la colección original (y lanza una excepción si lo intenta).

La otra ventaja foreaches que funciona en cualquiera IEnumerable, donde forsolo tiene sentido IList, donde cada elemento tiene un índice.

Sin embargo, si necesita usar el índice de un elemento, entonces, por supuesto, debería poder usar un forbucle. Pero si no necesita usar un índice, tener uno es simplemente abarrotar su código.

No hay implicaciones significativas de rendimiento hasta donde yo sé. En algún momento en el futuro, podría ser más fácil adaptar el código foreachpara ejecutar en múltiples núcleos, pero eso no es algo de qué preocuparse en este momento.


23
Ctford: No, no lo es. El compilador ciertamente no puede reordenar elementos foreach. foreachno está en absoluto relacionado con la programación funcional. Es un paradigma totalmente imperativo de programación. Atribuyes mal las cosas que suceden en TPL y PLINQ foreach.
Mehrdad Afshari

13
@BlueTrin: Ciertamente garantiza el pedido (la sección de especificaciones de C # 8.8.4 se define formalmente foreachcomo un equivalente de un whilebucle). Creo que sé a lo que @ctford se refiere. La biblioteca paralela de tareas permite que la colección subyacente proporcione elementos en un orden arbitrario (llamando .AsParallela un enumerable). foreachno hace nada aquí y el cuerpo del bucle se ejecuta en un solo hilo . Lo único que está paralelo es la generación de la secuencia.
Mehrdad Afshari

66
Enumerable.Select tiene una sobrecarga que le permite obtener el índice del elemento, por lo que incluso la necesidad de un índice no exige el uso de for. Ver msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill

55
ForEach es útil para facilitar la lectura y guardar la escritura. Sin embargo, los costos importan; cambiando 2 o 3 bucles for en una interfaz de usuario del diseñador de documentos que hice, de (para cada obj en la lista) a (para i = 0 a list.count-1) redujo el tiempo de respuesta de 2-3 segundos por edición a aproximadamente. 5 segundos por edición en un documento pequeño que se repite a través de unos cientos de objetos. Ahora, incluso para documentos de gran tamaño, no hay un aumento en el tiempo para repetir todo. No tengo idea de cómo sucedió esto. Lo que sí sé es que la alternativa era un esquema complicado para recorrer solo un subconjunto de los objetos. ¡Tomaré el cambio de 5 minutos cualquier día! - No micro-optimización.
FastAl

55
@FastAl La diferencia entre foreachy el forrendimiento de las listas normales es una fracción de segundo para iterar sobre millones de elementos, por lo que su problema ciertamente no estaba directamente relacionado con el rendimiento de cada uno, al menos no para unos pocos cientos de objetos. Suena como una implementación de enumerador rota en cualquier lista que estaba usando.
Mike Marynowski el

53

Cada vez que hay argumentos sobre el rendimiento, solo necesita escribir una pequeña prueba para que pueda usar resultados cuantitativos para respaldar su caso.

Use la clase StopWatch y repita algo unos millones de veces, para mayor precisión. (Esto podría ser difícil sin un bucle for):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

Los dedos cruzaron los resultados de este programa que muestran que la diferencia es insignificante, y que bien podría hacer lo que resulte en el código más fácil de mantener


13
Pero no se puede comparar el rendimiento de for y foreach. Se supone que deben usarse en diferentes circunstancias.
Michael Krelin - hacker

77
Estoy de acuerdo contigo Michael, no debes elegir cuál usar en función del rendimiento, ¡debes elegir el que tenga más sentido! Pero si su jefe dice "No lo use porque es más lento que foreach", entonces esta es la única manera de convencerlo de que la diferencia es insignificante
Rob Fonseca-Ensor

"(Esto podría ser difícil sin un bucle for)" O puede usar un bucle while.
jonescb

49

Siempre estará cerca. Para una matriz, a veces for es un poco más rápido, pero foreaches más expresivo y ofrece LINQ, etc. En general, quédese con foreach.

Además, foreachpuede optimizarse en algunos escenarios. Por ejemplo, una lista vinculada puede ser terrible por indexador, pero puede ser rápida por foreach. En realidad, el estándar LinkedList<T>ni siquiera ofrece un indexador por este motivo.


Entonces, ¿estás diciendo que LinkedList<T>es más delgado que List<T>? Y si siempre voy a usar foreach(en lugar de for), ¿es mejor usar LinkedList<T>?
JohnB

2
@JohnB: no es más delgado; Sólo diferente. Por ejemplo, cada nodo en una lista vinculada tiene referencias adicionales que no son necesarias para una matriz plana (que también apuntala List<T>). Es más que es más barato insertar / eliminar .
Marc Gravell

36

Supongo que probablemente no será significativo en el 99% de los casos, entonces, ¿por qué elegiría el más rápido en lugar del más apropiado (como el más fácil de entender / mantener)?


66
@klew, si realmente perfilas tu código no tendrías que adivinar qué 20% necesita ser lo más rápido posible. Probablemente también descubra que el número real de bucles que deben ser rápidos es mucho menor. Además, ¿realmente estás diciendo que el acto de bucle es donde pasas tu tiempo, en lugar de lo que realmente haces en ese bucle?
tster

32

Es poco probable que haya una gran diferencia de rendimiento entre los dos. Como siempre, cuando nos enfrentamos a un "¿cuál es más rápido?" pregunta, siempre debes pensar "puedo medir esto".

Escriba dos bucles que hagan lo mismo en el cuerpo del bucle, ejecute y cronometre ambos, y vea cuál es la diferencia en velocidad. Haga esto con un cuerpo casi vacío y un cuerpo de bucle similar a lo que realmente hará. También pruébelo con el tipo de colección que está utilizando, porque los diferentes tipos de colecciones pueden tener diferentes características de rendimiento.


32

Hay muy buenas razones para preferir foreach bucles sobre forbucles. Si puede usar un foreachbucle, su jefe tiene razón en que debería hacerlo.

Sin embargo, no todas las iteraciones simplemente pasan por una lista en orden una por una. Si él está prohibiendo , sí, eso está mal.

Si yo fuera tú, lo que haría sería convertir todos tus bucles naturales en recursividad . Eso le enseñaría, y también es un buen ejercicio mental para ti.


¿Cómo se compara la recursividad con los forbucles y los foreachbucles en cuanto al rendimiento?
JohnB

Depende. Si utiliza la recursividad de cola y su compilador es lo suficientemente inteligente como para darse cuenta, puede ser idéntico. OTOH: Si no es así y haces algo estúpido como pasar una gran cantidad de datos sin cambio (sin cambios) como parámetros o declarar grandes estructuras en la pila como locales, puede ser realmente muy lento (o incluso quedarse sin RAM).
TED

Ahhh Ya veo por qué preguntaste eso ahora. Esta respuesta fue a una pregunta totalmente diferente. Por alguna extraña razón, Jonathan Sampson fusionó los dos ayer. Realmente no debería haber hecho eso. Las respuestas combinadas no tendrán ningún sentido aquí.
TED

18

Jeffrey Richter en TechEd 2005:

"He aprendido a lo largo de los años que el compilador de C # es básicamente un mentiroso para mí". .. "Miente sobre muchas cosas". .. "Como cuando haces un bucle foreach ..." ... "... esa es una pequeña línea de código que escribes, pero lo que escupe el compilador de C # para hacer eso es fenomenal. intente / finalmente bloquee allí, dentro del bloque finalmente, convierte su variable en una interfaz IDisposable, y si la conversión tiene éxito, llama al método Dispose, dentro del ciclo llama a la propiedad Current y al método MoveNext repetidamente dentro del ciclo, se están creando objetos debajo de las cubiertas. Mucha gente usa foreach porque es una codificación muy fácil, muy fácil de hacer ... "..." foreach no es muy bueno en términos de rendimiento,

Webcast bajo demanda: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US


12

Esto es ridículo. No hay una razón convincente para prohibir el for-loop, el rendimiento inteligente u otro.

Vea el blog de Jon Skeet para un punto de referencia de rendimiento y otros argumentos.



La construcción de bucle que es más rápida depende de lo que necesita iterar. Otro blog que compara múltiples iteraciones sobre múltiples tipos de objetos , como DataRows y objetos personalizados. También incluye el rendimiento de la construcción del bucle While y no solo las construcciones de bucle for y foreach.
Codificador gratuito 24 de

11

En los casos en que trabaja con una colección de objetos, foreaches mejor, pero si incrementa un número, un forbucle es mejor.

Tenga en cuenta que en el último caso, podría hacer algo como:

foreach (int i in Enumerable.Range(1, 10))...

Pero ciertamente no funciona mejor, en realidad tiene un rendimiento peor en comparación con a for.


"Mejor" es discutible: es más lento y el depurador dnspy ​​no entrará en un foreach C # (aunque sí lo hará el depurador VS2017). A veces es más legible, pero si admite idiomas sin él, puede ser molesto.
Zeek2

10

Esto debería salvarte:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Utilizar:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Para una mayor victoria, puede tomar tres delegados como parámetros.


1
Una pequeña diferencia es que forgeneralmente se escribe un bucle para excluir el final del rango (p 0 <= i < 10. Ej .). Parallel.Fortambién lo hace para mantenerlo fácilmente intercambiable con un forbucle común .
Groo

9

puedes leer sobre esto en Deep .NET - parte 1 Iteración

cubre los resultados (sin la primera inicialización) desde el código fuente de .NET hasta el desmontaje.

por ejemplo: iteración de matriz con un bucle foreach: ingrese la descripción de la imagen aquí

y - enumerar iteración con foreach loop: ingrese la descripción de la imagen aquí

y los resultados finales: ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


8

Las diferencias de velocidad en a for- y a - foreachloop son muy pequeñas cuando recorres estructuras comunes como matrices, listas, etc., y hacer una LINQconsulta sobre la colección es casi siempre un poco más lento, ¡aunque es mejor escribir! Como decían los otros carteles, busque expresividad en lugar de un milisegundo de rendimiento adicional.

Lo que no se ha dicho hasta ahora es que cuando foreachse compila un bucle, el compilador lo optimiza en función de la colección sobre la que está iterando. Eso significa que cuando no esté seguro de qué bucle usar, debe usar el foreachbucle: generará el mejor bucle para usted cuando se compile. También es más legible.

Otra ventaja clave con el foreachbucle es que si la implementación de su colección cambia (de un int arraya un, List<int>por ejemplo), entonces su foreachbucle no requerirá ningún cambio de código:

foreach (int i in myCollection)

Lo anterior es el mismo sin importar de qué tipo sea su colección, mientras que en su forciclo, lo siguiente no se compilará si cambia myCollectionde arraya a List:

for (int i = 0; i < myCollection.Length, i++)

7

"¿Hay algún argumento que pueda usar para ayudarme a convencerlo de que el bucle for es aceptable?"

No, si tu jefe está microgestionando hasta el nivel de decirte qué construcciones de lenguaje de programación usar, realmente no hay nada que puedas decir. Lo siento.


7

Probablemente depende del tipo de colección que esté enumerando y la implementación de su indexador. Sin embargo, en general, foreaches probable que el uso sea un mejor enfoque.

Además, funcionará con cualquiera IEnumerable, no solo con indexadores.


7

Tiene las mismas dos respuestas que la mayoría de las preguntas "que es más rápido":

1) Si no mides, no lo sabes.

2) (Porque ...) Depende.

Depende de cuán costoso sea el método "MoveNext ()", en relación con lo costoso que es el método "this [int index]", para el tipo (o tipos) de IEnumerable sobre el que iterará.

La palabra clave "foreach" es la abreviatura de una serie de operaciones: llama a GetEnumerator () una vez en IEnumerable, llama a MoveNext () una vez por iteración, realiza algunas comprobaciones de tipo, etc. Lo más probable para impactar las mediciones de rendimiento es el costo de MoveNext () ya que se invoca O (N) veces. Quizás sea barato, pero quizás no lo sea.

La palabra clave "for" parece más predecible, pero dentro de la mayoría de los bucles "for" encontrará algo así como "colección [índice]". Esto parece una simple operación de indexación de matriz, pero en realidad es una llamada a un método, cuyo costo depende completamente de la naturaleza de la colección sobre la que está iterando. Probablemente sea barato, pero tal vez no lo sea.

Si la estructura subyacente de la colección es esencialmente una lista vinculada, MoveNext es muy barato, pero el indexador puede tener un costo O (N), lo que hace que el costo real de un bucle "for" O (N * N).


6

Cada construcción de lenguaje tiene un momento y lugar apropiados para su uso. Hay una razón por la cual el lenguaje C # tiene cuatro declaraciones de iteración separadas : cada una está ahí para un propósito específico y tiene un uso apropiado.

Recomiendo sentarse con su jefe y tratar de explicar racionalmente por qué un forciclo tiene un propósito. Hay momentos en que un forbloque de iteración describe más claramente un algoritmo que una foreachiteración. Cuando esto es cierto, es apropiado usarlos.

También le diría a su jefe: el rendimiento no es, y no debería ser un problema de ninguna manera práctica, es más una cuestión de expresión del algoritmo de una manera sucinta, significativa y sostenible. Las micro optimizaciones como esta pierden por completo el punto de la optimización del rendimiento, ya que cualquier beneficio real del rendimiento vendrá del rediseño algorítmico y la refactorización, no de la reestructuración en bucle.

Si, después de una discusión racional, todavía existe este punto de vista autoritario, depende de usted cómo proceder. Personalmente, no sería feliz trabajando en un entorno donde se desalienta el pensamiento racional, y consideraría mudarme a otro puesto bajo un empleador diferente. Sin embargo, recomiendo encarecidamente la discusión antes de enfadarse; puede que haya un simple malentendido.


5

Es lo que haces dentro del bucle lo que afecta el rendimiento, no la construcción de bucle real (suponiendo que tu caso no sea trivial).


5

Si fores más rápido de lo que foreachrealmente es, además del punto. Dudo seriamente que elegir uno sobre el otro tenga un impacto significativo en su rendimiento.

La mejor manera de optimizar su aplicación es mediante la creación de perfiles del código real. Eso determinará los métodos que representan la mayor parte del trabajo / tiempo. Optimizar esos primero. Si el rendimiento aún no es aceptable, repita el procedimiento.

Como regla general, recomendaría mantenerse alejado de las micro optimizaciones, ya que rara vez producirán ganancias significativas. La única excepción es cuando se optimizan las rutas activas identificadas (es decir, si su perfil identifica algunos métodos muy utilizados, puede tener sentido optimizarlos ampliamente).


Si el único tipo de optimización que necesito hacer en los proyectos en los que trabajo son micro optimizaciones, sería un campista feliz. Lamentablemente, este nunca es el caso.
Yannick Motton

2
fores marginalmente más rápido que foreach. Me opondría seriamente a esta declaración. Eso depende totalmente de la colección subyacente. Si una clase de lista vinculada proporciona un indexador con un parámetro entero, esperaría que el uso de un forbucle sea O (n ^ 2) mientras que foreachse espera que sea O (n).
Mehrdad Afshari

@Merhdad: En realidad, ese es un buen punto. Estaba pensando en el caso habitual de indexar una lista (es decir, una matriz). Reformularé para reflejar eso. Gracias.
Brian Rasmussen

@Mehrdad Afshari: indexar una colección por entero puede ser mucho más lento que enumerarlo. Pero en realidad está comparando el uso for y una búsqueda de indexador con el uso foreachpor sí mismo. Creo que la respuesta de @Brian Rasmussen es correcta que, aparte de cualquier uso con una colección, forsiempre será un poco más rápido que foreach. Sin embargo, formás una búsqueda de colección siempre será más lenta que foreachpor sí sola.
Daniel Pryden

@Daniel: O tienes una matriz simple, para la cual ambos generarán un código idéntico, o hay un indexador involucrado cuando usas una fordeclaración. El forbucle simple con una variable de control de enteros no es comparable a foreach, por lo que está fuera. Entiendo lo que @Brian quiere decir y es correcto como dices, pero la respuesta puede ser engañosa. Re: su último punto: no, en realidad, forterminar List<T>es aún más rápido que foreach.
Mehrdad Afshari

4

Los dos correrán casi exactamente de la misma manera. Escriba un código para usar ambos, luego muéstrele el IL. Debe mostrar cálculos comparables, lo que significa que no hay diferencia en el rendimiento.


El compilador reconoce los bucles foreach utilizados en matrices / listas IL, etc. y los cambia a bucles for.
Callum Rogers

3
Muéstrele líneas de prueba ininteligible de que está bien y solicite su prueba de que no está bien.
cjk

3

tiene una lógica más simple de implementar, por lo que es más rápido que foreach.


3

A menos que esté en un proceso de optimización de velocidad específico, diría que use el método que produzca el código más fácil de leer y mantener.

Si un iterador ya está configurado, como con una de las clases de colección, entonces el foreach es una buena opción fácil. Y si está iterando un rango entero, entonces probablemente sea más limpio.



3

En la mayoría de los casos, realmente no hay diferencia.

Por lo general, siempre tiene que usar foreach cuando no tiene un índice numérico explícito, y siempre tiene que usar para cuando realmente no tiene una colección iterable (por ejemplo, iterar sobre una cuadrícula de matriz bidimensional en un triángulo superior) . Hay algunos casos en los que tiene una opción.

Se podría argumentar que los bucles for pueden ser un poco más difíciles de mantener si los números mágicos comienzan a aparecer en el código. Debería tener razón al estar molesto por no poder usar un bucle for y tener que construir una colección o usar una lambda para construir una subcolección solo porque los bucles for han sido prohibidos.


3

Parece un poco extraño prohibir totalmente el uso de algo así como un bucle for.

Hay un artículo interesante aquí. que cubre una gran cantidad de las diferencias de rendimiento entre los dos bucles.

Personalmente, creo que foreach es un poco más legible para los bucles, pero debe usar lo mejor para el trabajo en cuestión y no tener que escribir un código extralargo para incluir un bucle foreach si un bucle for es más apropiado.


Cita esencial del artículo al que se vincula: "... si planea escribir código de alto rendimiento que no sea para colecciones, use para bucle. Incluso para colecciones, foreach puede ser útil cuando se usa, pero no es tan eficiente".
NickFitz

3

Encontré el foreachciclo que itera a través de un List más rápido . Vea los resultados de mi prueba a continuación. En el código de abajo I iterar una arrayde tamaño 100, 10000 y 100000 por separado utilizando fory foreachbucle para medir el tiempo.

ingrese la descripción de la imagen aquí

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

ACTUALIZADO

Después de la sugerencia de @jgauffin, utilicé el código de @johnskeet y descubrí que el forbucle con arrayes más rápido que el siguiente,

  • Bucle Foreach con matriz.
  • Para bucle con lista.
  • Foreach loop con lista.

Vea los resultados de mi prueba y el código a continuación,

ingrese la descripción de la imagen aquí

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

3
Esta es una prueba muy pobre. a) haces muy pocas iteraciones para obtener una respuesta concluyente b) That Thread.Sleep realmente no esperará un milisegundo. Use el mismo método que Jon Skeet usó en su respuesta.
jgauffin

1
El 99.99% del tiempo se gasta definitivamente en el thread.sleep (lo que no garantiza qué tan rápido volverá, excepto que no lo hará antes de al menos ese tiempo). El bucle es muy rápido y el sueño es muy lento, no usa el último para probar el primero.
Ronan Thibaudau

3

Realmente puedes atornillar su cabeza e ir por un cierre IQueryable .foreach en su lugar:

myList.ForEach(c => Console.WriteLine(c.ToString());

3
Reemplazaría su línea de código con myList.ForEach(Console.WriteLine).
Mehrdad Afshari

2

No esperaría que nadie encontrara una "enorme" diferencia de rendimiento entre los dos.

Supongo que la respuesta depende de si la colección a la que está intentando acceder tiene una implementación de acceso de indexador más rápida o una implementación de acceso de IEnumerator más rápida. Como IEnumerator a menudo usa el indexador y solo contiene una copia de la posición actual del índice, esperaría que el acceso al enumerador sea al menos tan lento o más lento que el acceso directo al índice, pero no mucho.

Por supuesto, esta respuesta no tiene en cuenta las optimizaciones que el compilador puede implementar.


El compilador de C # hace muy poca optimización, realmente lo deja al JITter.
ljs

Bueno, el JITter es un compilador ... ¿verdad?
JohannesH

2

Tenga en cuenta que el bucle for y el bucle foreach no siempre son equivalentes. Los enumeradores de listas arrojarán una excepción si la lista cambia, pero no siempre obtendrá esa advertencia con un ciclo for normal. Incluso puede obtener una excepción diferente si la lista cambia en el momento equivocado.


Si la lista está cambiando debajo de usted, entonces no puede confiar en que el enumerador respalde un bucle foreach para decirle eso. No volverá a verificar después de que le devuelva el valor, lo que resulta en una carrera.
hoodaticus
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.