Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Programando videojuegos: Tutorial XNA Game Studio 4.0

Iniciado por wardamo, 09 de Junio de 2010, 11:47:40 AM

« anterior - próximo »

Vicente

No decimos que no estén por algún motivo, pero no quita que sigan estando mal o sean cuestionables, hay formas mejor de hacer las cosas.

En los arrays el problema es que ese tipo de código en la Xbox va a saltos. Puedes tener un sitio general donde guardas arrays con el tamaño de las entidades colisionables y reutilizarlos (una clase estática, un singleton,...). Es mucho mejor para la Xbox usar mucha memoria siempre que pedirla poco a poco de forma continuada, el garbage collector es muy simple en la consola y ese tipo de cosas le sientan fatal. En PC no lo vas a notar porque el GC es diferente y es mucho más listo.

Sobre lo del foreach, sigue estando mal de concepto: un bucle foreach (o el método ForEach) no debería modificar la colección como dices que haces. Si necesitas borrar tendrás que hacerlo de otra forma. Fíjate en el código del método ForEach de Microsoft:


public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}


Seguramente tienes un bug en tu código porque al ir borrando elementos de la lista el size cambia y los últimos no se visitan. Y esto es así porque no se espera que action modifique el tamaño de la lista, no debería hacerlo.

Y lo demás es nomenclatura que en .NET está claramente estandarizada. Tienes las "Guidelines for Names" dentro de la "Design Guidelines for Developing Class Libraries" de Microsoft:

http://msdn.microsoft.com/en-us/library/ms229002.aspx

Y tienes herramientas que te ayudan a detectar que normas de nomenclatura y diseño no cumples y como corregirlas de forma automática al compilar:

http://code.msdn.microsoft.com/sourceanalysis

Ale, un saludo!

Vicente

[EX3]

Cita de: Vicente en 09 de Julio de 2010, 11:20:51 AM
Sobre lo del foreach, sigue estando mal de concepto: un bucle foreach (o el método ForEach) no debería modificar la colección como dices que haces. Si necesitas borrar tendrás que hacerlo de otra forma.
Al margen de que este mal o no, a mi en su dia me toco demasiado las narices el no poder eliminar elementos mientras recorria una coleccion con un foreach en .NET cuando en el infame Visual Basic 6.0 siempre lo he podido hacer sin problema alguno. Borrabas el elemento actual y el cursor automaticamente se situaba en el siguiente. En .NET me toco hacer dos pasadas, una para seleccionar los indices de los elementos a borrar y otra para ir uno a uno borrando elementos por su indice, lo que me parecio un tanto feo :P Ya digo, no se si es correcto o no hacer esto como norma general en el resto de lenguajes, pero desde mi punto de vista, si con el tiempo se van actualizando los lenguajes de programacion para facilitar tareas que antaño eran tediosas y complejas, el poder eliminar al vuelo un elemento de una coleccion desde un foreach que lo recorra deberia ser algo mas que posible y basico ;)

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Vicente

#17
Un bucle foreach representa un iterador. Un iterador es un patrón que te permite recorrer de forma uniforme diferentes estructuras de datos. En .NET esto significa que las estructuras que quieres iterar tienen que implementar una interfaz llamada IEnumerable, y si os fijáis la interfaz IEnumerable no tiene ningún método llamado Remove. Es decir, esto:


IEnumerable<object> list = CreateList();
foreach (object o in list)
{
   list.Remove(o);
}


No compila. Es un error de concepto querer modificar la colección que se itera en un foreach, porque no tienes ni idea de si esa colección permite la modificación, ya que está oculta bajo un IEnumerable. Por ejemplo esta función crea una lista infinita de números positivos:


public static IEnumerable<int> InfiniteList()
{
   int i = 0;
   while (true)
       yield return i++;
}


¿Qué sentido tiene el querer borrar un elemento de esa colección?

Si quieres borrar elementos de una lista recórrela con un bucle for invertido (del último al primero) y listo, más sencillo imposible. Pero pedirle a un foreach que permita hacer eso es no entender cual es la responsabilidad de un iterador.

Un saludo!

Vicente

P.D.: obviamente VB6 es infame y por eso dejaba hacer cosas infames como esa :p

[EX3]

Cita de: Vicente en 09 de Julio de 2010, 01:02:20 PM
P.D.: obviamente VB6 es infame y por eso dejaba hacer cosas infames como esa :p
xDDDD Bueno, oviando lo ovio :P a mi me sucede a veces que implemento un codigo para recorrer una lista de elementos a los cuales les aplico un criterio concreto para una accion llamemosla x. Ese criterio puede dar como resultado que el elemento ejecute un metodo, por ejemplo, o bien que se descarte o se anule, y precise cepillarmelo de la lista por que ya no sirve por x o y. A mi me resulta mas comodo cepillarmelo en ese mismo momento que ya lo tengo localizado, teniendo asi que evitarme hacer otra pasa a la lista para eliminar todos los elementos que haya podido descartar utilizando otra lista mas para almacenar los indices de cada elemento descartado. Si la solucion es que deberia usar un for en vez de un foreach te diria que a mi como programador, me resulta mas comodo utilizar un foreach que un for por razones obvias, y mas todavia si estamos hablando de un lenguaje de alto nivel de abstraccion en vez de uno de bajo nivel como podria ser C++. Piensa que este caso es intuitivo y le ha pasado a mucha gente.

Si en .NET el problema es que la interfaz IEnumerable no implementa dicho metodo Remove() quizas se deba diseñar otra inferfaz que si lo permitiera. No se, es que si no veo dar muchas vueltas para algo tan sencillo como es simplemente eliminar un elemento y pasar el cursor al siguiente :P

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Vicente

#19
Sigues sin entenderlo :( No van a hacer un enumerador que permita borrar porque es imposible de hacer: el iterador resuelve el problema de poder recorrer de la misma forma (foreach) cosas que son muy diferentes (una lista, un árbol, un heap,...). Y nada obliga a que esas estructuras sean modificables, es un problema totalmente diferente.

Es decir, si mi problema es tener que recorrer algo, por ejemplo un List, es mejor usar foreach en vez de for ya que si más adelante cambio de List a por ejemplo Tree, mi código de recorrer seguiría funcionando sin tener que cambiar ni una línea. Si usara un for seguramente mi código no funcionaría porque para un árbol un bucle for no tiene sentido.

Pero si tu problema es que quieres modificar una colección, foreach no es lo que buscas porque foreach asume que las colecciones son inmutables. Si quieres usar foreach, trabaja con esa idea en la cabeza y genera una nueva colección de cosas a partir de la vieja (igual que al modificar un string lo que haces realmente es generar un string nuevo porque los strings son inmutables).

La confusión de mucha gente (supongo) es que usas como clase List, y como List permite borrar, pero además implementa IEnumerable con lo que se puede usar en un foreach, pues parece lógico el juntar las dos cosas y borrar en el foreach. Pero de lógico no tiene nada.

Un saludo!

Vicente

[EX3]

Cita de: Vicente en 09 de Julio de 2010, 01:43:15 PM
Sigues sin entenderlo :( No van a hacer un enumerador que permita borrar porque es imposible de hacer: el iterador resuelve el problema de poder recorrer de la misma forma (foreach) cosas que son muy diferentes (una lista, un árbol, un heap,...). Y nada obliga a que esas estructuras sean modificables, es un problema totalmente diferente.
Vale, este punto si lo he entendido, pero antes no me he explicado bien creo (creo que me estas explicando que el enumerador no tiene capacidad de eliminar el elemento de la lista que recorre). En mi caso, creo que no deberia importar que IEnumerable implementase o no un metodo Remove(), yo elimino el elemento llamando a la propia coleccion desde dentro del foreach:
object lista[];

foreach object ite in lista
{
    ...
    lista.Remove(ite);
    ...
}

Se supone que ahi no deberia intervenir el iterador salvo para pasar la referencia del elemento de la lista, no? ???

Cita de: Vicente en 09 de Julio de 2010, 01:43:15 PM
La confusión de mucha gente (supongo) es que usas como clase List, y como List permite borrar, pero además implementa IEnumerable con lo que se puede usar en un foreach, pues parece lógico el juntar las dos cosas y borrar en el foreach. Pero de lógico no tiene nada.
Olvidandote de si es un List<> o un Colecction o un simple Array, el programador de calle solo ve que tiene una lista de elementos que puede recorrer comodamente con un foreach en donde el iterador toma la instancia del objeto que representa el elemento. Con ello al igual que puede acceder al objeto y sus metodos entiende que puede llamar in situ a la propia coleccion, lista o array para decirle en ese instante "borrame el objeto al que el iterador hace referencia". A eso me refiero con intuitivo que no logico (estoy seguro que muchas cosas que hago rompen con el esquema logico de cualquier programador xDDDDDDDDDDDDDDDDDDDDD). Igualmente entiendo que en la mayoria de los casos (por ejemplo, un metodo para buscar y eliminar un elemento de la lista) un simple for bastaria de sobra.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Vicente

Cita de: [EX3] en 09 de Julio de 2010, 02:07:30 PM
Vale, este punto si lo he entendido, pero antes no me he explicado bien creo (creo que me estas explicando que el enumerador no tiene capacidad de eliminar el elemento de la lista que recorre). En mi caso, creo que no deberia importar que IEnumerable implementase o no un metodo Remove(), yo elimino el elemento llamando a la propia coleccion desde dentro del foreach:
object lista[];

foreach object ite in lista
{
    ...
    lista.Remove(ite);
    ...
}

Se supone que ahi no deberia intervenir el iterador salvo para pasar la referencia del elemento de la lista, no? ???

Sí que importa: foreach trabaja sobre IEnumerables que son enumeraciones de elementos. A ti te parece que no es un IEnumerable porque sabes que tres líneas más arriba tienes que es una lista o un array o lo que sea, pero para el foreach es un IEnumerable. Ten en cuenta que un bucle for y un bucle foreach se traducen en cosas muy diferentes al compilarse. Un foreach se traduce a algo como esto:


IEnumerator<object> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    object o = enumerator.Current;
    // hacer algo con o
}


Y ahí el framework no tiene ni idea de si el IEnumerable originalmente era un List, un array, un yield,...

Cita de: [EX3] en 09 de Julio de 2010, 02:07:30 PM
Olvidandote de si es un List<> o un Colecction o un simple Array, el programador de calle solo ve que tiene una lista de elementos que puede recorrer comodamente con un foreach en donde el iterador toma la instancia del objeto que representa el elemento. Con ello al igual que puede acceder al objeto y sus metodos entiende que puede llamar in situ a la propia coleccion, lista o array para decirle en ese instante "borrame el objeto al que el iterador hace referencia". A eso me refiero con intuitivo que no logico (estoy seguro que muchas cosas que hago rompen con el esquema logico de cualquier programador xDDDDDDDDDDDDDDDDDDDDD). Igualmente entiendo que en la mayoria de los casos (por ejemplo, un metodo para buscar y eliminar un elemento de la lista) un simple for bastaria de sobra.

Tu tienes una lista, array,... de elementos fuera del foreach, pero dentro del foreach lo que tienes es una enumeración de elementos.

blau


Yo implemento mi version de  IDisposable en mis objetos.

Al añadir un elemento a una coleccion, me engancho al evento  Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update()  recorres la lista ToRemove y eliminas todas las instancias.

Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)



[EX3]

@Blau: interesante idea, me la apunto para futuras implementaciones :)

@Vicente: gracias por la explicacion. Ahora entiendo un poco mas el mecanismo del IEnumerable en los foreach de .NET y esas restricciones tecnicas que me comentas. Lo del VB6.0 supongo que tendra sentido al trabajar unicamente con colecciones genericas (foreach no nos sirve para arrays por ejemplo). Supongo que al tener que trabajar solo con una estructura unica y simple no habran tenido problema de adaptar su implementacion del iterador para tener control completo sobre la coleccion, permitiendo la eliminacion de elementos por ejemplo. En fin, VB6.0 y sus malas artes xDDDD

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Vicente

Un último ejemplo para ver si lo entiendes mejor.

Un foreach/IEnumerable es el equivalante en base de datos a un SELECT. Tu lo que me estás diciendo es que en medio de un SELECT quieres hacer un UPDATE o un DROP o un DELETE. Lo mismo en BBDD se puede hacer semejante cosa (ando un poco oxidao de SQL), pero estoy tambión seguro que cualquier DBA te cortaría las manos por escribir una query así :p Si haces un SELECT es porque quieres obtener un subconjunto de datos.

En .NET directamente no te dejan, por si acaso :p

Vicente

Cita de: blau en 09 de Julio de 2010, 05:03:12 PM

Yo implemento mi version de  IDisposable en mis objetos.

Al añadir un elemento a una coleccion, me engancho al evento  Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update()  recorres la lista ToRemove y eliminas todas las instancias.

Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)

Lo mismo esta clase te valdría para tu GenericCollection: ObservableCollection

http://msdn.microsoft.com/en-us/library/ms668604.aspx

[EX3]

Cita de: Vicente en 09 de Julio de 2010, 10:27:39 PM
Un último ejemplo para ver si lo entiendes mejor.
El concepto de foreach, crear una enumeracion de una lista de elementos, lo tengo claro, Vicente. En VB6.0 igualmente se genera una enumeracion a parte de la coleccion a la que referencia mediante esta implementacion en las clases que hacen de colecciones personalizadas:
Public Property Get NewEnum() As IUnknown
   'esta propiedad permite enumerar
   'esta colección con la sintaxis For...Each
   Set NewEnum = varCol.[_NewEnum]
End Property

Que en .NET no te dejen por que ven logico que recorrer una coleccion no debe permitir interactuar con ella, bueno, aceptamos barco con tu ejemplo del SQL, pero como todo, si tecnicamente es posible, deberian dejar la puerta abierta a esa posibilidad a cuenta y riesgo del programador por que por ejemplo a mi me sigue parecio mas logico e intuitivo operar desde un bucle que tener que hacerme una lista con los elementos descartados para luego tener que recorrer esa lista cargandome los elementos antes visitados, en resumen, hacer dos vueltas (gasto de tiempo) y gastar mas memoria (dos listas en vez de una), esto lo veo dar vueltas como un tonto, pero bueno.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

blau

Cita de: Vicente en 09 de Julio de 2010, 10:32:11 PM
Cita de: blau en 09 de Julio de 2010, 05:03:12 PM

Yo implemento mi version de  IDisposable en mis objetos.

Al añadir un elemento a una coleccion, me engancho al evento  Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update()  recorres la lista ToRemove y eliminas todas las instancias.

Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)

Lo mismo esta clase te valdría para tu GenericCollection: ObservableCollection

http://msdn.microsoft.com/en-us/library/ms668604.aspx

Sabia que tenia que existir...  :o estuve buscando y no lo encontre.   Tomo nota... :)

Me pasa igual que con el delegado Func, sabia que tenia que estar definido , al igual que Action y Predicate pero no lo encontraba hasta que lo vi en un código.


Vicente

Cita de: blau en 10 de Julio de 2010, 12:18:27 AM
Sabia que tenia que existir...  :o estuve buscando y no lo encontre.   Tomo nota... :)

Me pasa igual que con el delegado Func, sabia que tenia que estar definido , al igual que Action y Predicate pero no lo encontraba hasta que lo vi en un código.

Ya, el framework es infinito :(

Vicente

Cita de: [EX3] en 09 de Julio de 2010, 11:00:06 PM
Cita de: Vicente en 09 de Julio de 2010, 10:27:39 PM
Un último ejemplo para ver si lo entiendes mejor.
El concepto de foreach, crear una enumeracion de una lista de elementos, lo tengo claro, Vicente. En VB6.0 igualmente se genera una enumeracion a parte de la coleccion a la que referencia mediante esta implementacion en las clases que hacen de colecciones personalizadas:
Public Property Get NewEnum() As IUnknown
   'esta propiedad permite enumerar
   'esta colección con la sintaxis For...Each
   Set NewEnum = varCol.[_NewEnum]
End Property

Que en .NET no te dejen por que ven logico que recorrer una coleccion no debe permitir interactuar con ella, bueno, aceptamos barco con tu ejemplo del SQL, pero como todo, si tecnicamente es posible, deberian dejar la puerta abierta a esa posibilidad a cuenta y riesgo del programador por que por ejemplo a mi me sigue parecio mas logico e intuitivo operar desde un bucle que tener que hacerme una lista con los elementos descartados para luego tener que recorrer esa lista cargandome los elementos antes visitados, en resumen, hacer dos vueltas (gasto de tiempo) y gastar mas memoria (dos listas en vez de una), esto lo veo dar vueltas como un tonto, pero bueno.

Salu2...

Si me respondes eso es porque sigues sin entenderlo :( Pero más ejemplos ya no se me ocurren, solo me queda linkarte a la MSDN y que te la creas porque sí.

http://msdn.microsoft.com/en-us/library/ttw7t8t6%28VS.71%29.aspx

Citar
The foreach statement repeats a group of embedded statements for each element in an array or an object collection. The foreach statement is used to iterate through the collection to get the desired information, but should not be used to change the contents of the collection to avoid unpredictable side effects.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.