while loop hacer for ejemplo como ciclo c# for-loop code-review

hacer - for loop step c#



C#- No te metas con el índice de ciclo (11)

Bueno, esto agrega confusión por poco; podrías escribir tan fácilmente como:

while(MyControl.TabPages.Count > 0) { MyControl.TabPages.Remove(MyControl.TabPages[0]); }

o (más simple)

while(MyControl.TabPages.Count > 0) { MyControl.TabPages.RemoveAt(0); }

o (más simple)

MyControl.TabPages.Clear();

En todo lo anterior, no tengo que entrecerrar los ojos y pensar en ningún caso extremo; está bastante claro qué pasa cuando. Si está modificando el índice de bucle, rápidamente puede hacer que sea bastante difícil de entender de un vistazo.

Uno de los elementos de la lista de verificación de Steve McConnell es que no debe simular el índice de bucle (Capítulo 16, página 25, Índices de Bucle , formato PDF).

Esto tiene un sentido intuitivo y es una práctica que siempre he seguido, excepto tal vez como aprendí a programar de nuevo en el día.

En una revisión reciente del código, encontré este circuito incómodo y de inmediato lo marqué como sospechoso.

for ( int i=0 ; i < this.MyControl.TabPages.Count ; i++ ) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] ); i--; }

Es casi divertido ya que logra funcionar manteniendo el índice en cero hasta que se eliminen todas las TabPages.

Este bucle podría haberse escrito como

while(MyControl.TabPages.Count > 0) MyControl.TabPages.RemoveAt(0);

Y dado que el control fue, de hecho, escrito aproximadamente al mismo tiempo que el bucle, podría haber sido escrito como

MyControl.TabPages.Clear();

Desde entonces, me han desafiado sobre el tema de la revisión del código y descubrí que mi articulación de por qué es una mala práctica no era tan fuerte como me hubiera gustado. Dije que era más difícil comprender el flujo del ciclo y, por lo tanto, era más difícil de mantener y depurar y, en última instancia, más costoso a lo largo de la vida útil del código.

¿Hay una mejor articulación de por qué esta es una mala práctica?


Creo que podría construir un argumento más sólido al invocar los conceptos de alfabetización de Knuth, que los programas no deberían escribirse para computadoras, sino para comunicar conceptos a otros programadores , por lo tanto, el ciclo más simple:

while (this.MyControl.TabPages.Count>0) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] ); }

ilustra más claramente el intento: elimine la primera página de pestañas hasta que no quede ninguna. Creo que la mayoría de la gente asimilaría eso mucho más rápido que el ejemplo original.


Creo que señala el hecho de que las iteraciones de bucle están siendo controladas no por el "i ++" como cualquiera esperaría, sino por la alocada configuración "i--" que debería haber sido suficiente.

También creo que alterar el estado de "yo" al evaluar el conteo y luego alterar el conteo en el ciclo también puede conducir a problemas potenciales. Yo esperaría que un bucle for tenga generalmente un número "fijo" de iteraciones y la única parte de la condición for loop que cambia sea la variable "i" del bucle.


Creo que tu articulación es genial. Tal vez puede ser redactado así:

Dado que la lógica se puede expresar mucho más clara, debería.


El código original es altamente redundante para doblar la acción del for-loop a lo que es necesario. El incremento es innecesario y está equilibrado por la disminución. Esos deben ser incrementos PRE, no incrementos POST también, porque conceptualmente el incremento posterior es incorrecto. La comparación con el recuento de tabpages es semi redundante, ya que es una forma malintencionada de verificar que el contenedor esté vacío.

En resumen, es una habilidad innecesaria, agrega más que elimina la redundancia. Como puede ser obviamente más simple y obviamente más corto, está mal.


Esto podría ser más claro:

while (this.MyControl.TabPages.Count > 0) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] ); }


Estoy de acuerdo con tu desafío Si quieren mantener un ciclo for, el código:

for ( int i=0 ; i < this.MyControl.TabPages.Count ; i++ ) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] ); i--; }

reduce de la siguiente manera:

for ( int i=0 ; i < this.MyControl.TabPages.Count ; ) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] ); }

y luego a:

for ( ; 0 < this.MyControl.TabPages.Count ; ) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] ); }

Pero un ciclo while o un método Clear () , si eso existe, son claramente preferibles.


No estoy seguro si entiendo completamente lo que buscas, pero tal vez algo así?

for ( int i=this.MyControl.TabPages.Count - 1 ; i >= 0 ; i-- ) { this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] ); }

o simplemente esto de la colección TabPages implementa IList:

this.MyControl.TabPages.Clear();


Se trata de expectativas.

Cuando uno usa un contador de bucles, espera que se incremente (disminuya) cada iteración del bucle con la misma cantidad.

Si te metes (o mono si quieres) con el contador de bucles, tu bucle no se comporta como se esperaba. Esto significa que es más difícil de entender y aumenta las posibilidades de que su código sea malinterpretado, y esto introduce errores. O para (mis) citar un personaje sabio pero ficticio:

complexity leads to misunderstanding misunderstanding leads to bugs bugs leads to the dark side.


Un argumento que podría usarse es que es mucho más difícil depurar dicho código, donde el índice se cambia dos veces.


La única razón para molestarse con un índice sería si uno borrara cosas selectivamente. Incluso en ese caso, creo que sería preferible decir:

i=0; while(i < MyControl.Tabpages.Count) if (wantToDelete(MyControl.Tabpages(i)) MyControl.Tabpages.RemoveAt(i); else i++; en lugar de confundir el índice del ciclo después de cada eliminación. O, mejor aún, haga que el índice cuente hacia abajo, de modo que cuando se elimine un elemento, no afecte al índice de artículos futuros que necesitan ser eliminados. Si se eliminan muchos elementos, esto también puede ayudar a minimizar la cantidad de tiempo que se pasa moviendo elementos después de cada eliminación.