https://foro.gambas-es.org/viewtopic.php?f=1&t=1563
-----------------------------------
santijav
Martes, 19 Abril 2011, 00:38

Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Buenos días,

Leyendo hace algún tiempo atrás el siguiente link:

http://foro.gambas-es.org/viewtopic.php?f=1&t=1467

se me abrió la cabeza acerca de cómo realizar la aplicación que tengo en mente....

Tal como sugieren en ese link tengo una clase (denominada lote) que tiene los siguientes elementos:

[code linenumbers=false syntax=gambas]PUBLIC nombre AS String
PUBLIC pcompra AS Float
PUBLIC pventa AS Float
PUBLIC pultimo AS Float

PUBLIC SUB leevalores(xmlnombre AS String, xmlpcompra AS Float, xmlpventa AS Float, xmlpultimo AS Float)
  
  nombre = xmlnombre
  pcompra = CFloat(xmlpcompra)
  pventa = CFloat(xmlpventa)
  pultimo = CFloat(xmlpultimo)

END[/code]

Asimismo tengo un módulo donde creo una NEW collection o bien un NEW object[] (he probado con los dos, pudiendo acceder mediante los index teniendo el mismo problema)...


Pues resulta que leyendo un xml tomo unos atributos que se los paso a un objeto del tipo lote mediante el procedure leevalores.... Todo va bien.

Luego hago un modulo.nombredelobject[].add(objetodetipolote) y obviamente me lo agrega....

El tema es que cuando quiero acceder a los objetos almacenados en el array de objetos (o bien en la collection cuando probé de esa forma) resulta que todos los objetos almacenados tienen el mismo valor: el del último que asigné y guardé....

Es como si todos los objetos que fui guardando... en lugar de dejar guardados el valor que tenían cuando los agregué al object[] fueron actualizando los valores de sus elementos a los nuevos datos que se fueron leyendo del xml.... 

No se si esto se debe a que estoy declarando mal el ámbito de alguna variable o errando en alguna metodología....

Cualquier idea de acerca de dónde puedo seguir investigando para corregir esto será sumamente agradecida!


Si necesitan algún fragmento de código adicional lo subo sin problemas.

He aprendido mucho leyendo inquietudes de otros, este es el primer post que hago así que todavía no le tengo tomada la mano al sistema para foros, jeje.
Muchas gracias!
Saludos.
Santiago.


-----------------------------------
soplo
Martes, 19 Abril 2011, 03:41

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Hola

La parte de código que no pones es la que en mi opinión deberías poner.

Mas o menos debería ser algo como esto

[code linenumbers=false syntax=gambas]private sub LeerXml()
dim L as Lote 'definir la variable de clase Lote
Dim O as new object[] 'definir el espacio para el conjunto de lotes
Dim Nombre as string,  PrecioCompra as string,  PrecioVenta as string, PrecioUltimo as string

do while condicion 'mientras este bucle siga, leerás y meterás los datos en el object
   'aquí haces la extracción de datos de donde los saques
   ...
   'y obtienes Nombre,  preciocompra, precioventa, precioultimo
   ...

   'ahora viene la parte importante
   
   L=NEW Lote 'crear una nueva instancia
   L.InsertarValores(Nombre, Preciocompra, Precioventa, PrecioUltimo) 'asignar datos
   O.add(L) 'insertar el lote en el conjunto de lotes
   L.delete 'eliminar lote (No es necesario)
loop
end[/code]
Lo que hace ese bucle es ir leyendo del xml o lo que sea y va obteniendo un nombre, preciocompra, etc
Luego crea un lote, le asigna los valores y lo inserta en el conjunto de lotes
Ya puedes prescindir del lote porque está en el conjunto de lotes. Lo pongo para remarcar que no necesitas ese lote porque ya lo tienes guardado en el conjunto de lotes. Cuando quieras en otra parte rcuperar el lote N puedes hacer
[code linenumbers=false syntax=gambas]Dim L as new Lote
L=Obj[N)
[/code]

Por otra parte esa forma que has utilizado para enviar parámetros a la clase lote la veo incorrecta (aunque funciona)  y te explico la razón.

Si defines las variables como Public puedes acceder a ellas directamente, no necesitas un método para darles valor.  Podrías haber definido esas variables como private y funcionaría igual (mas limpio) o bien podrías haberlas definido públicas y darles valor directamente en cuyo caso no necesitas el método leevalores.

Prueba porque manejar estas cosas ayuda mucho. 

Por último hay otro método para pasar variables a una clase lote que es crearle propiedades a esa clase. En este caso lo veo por completo innecesario pero en otros casos es extremadamente útil y te aconsejo que lo mires.

Puse un ejemplo en este lugar, echa un vistazo
[url=http://foro.gambas-es.org/viewtopic.php?f=1&t=1557&p=9053#p9053]Enlace

Manejar estas técnicas para pasar datos de una clase a otra, de un formulario a otro (un formulario es una clase), etc es muy muy interesante y muy sencillo.

Un saludo
 :mryellow:


-----------------------------------
santijav
Martes, 19 Abril 2011, 14:37

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Estimado Soplo,

Muchísimas gracias por tu prontísima respuesta. :mryellow: 

La principal diferencia entre tu código y el mío  que veo es que instanciamos el objeto de tipo lote en distintos lugares. Vos lo hacés dentro del bucle y después de tener cada valor y yo lo hago al principio de todo... incluso antes de meterme en el bucle... Será eso el problema?

El tema de las variables públicas en el objeto lote... los creé así ya que esos valores son importantes para mi programa y necesito accederlos desde distintos lugares del programa. Sería mejor declararlas private y crear una función que me devuelva el valor que necesito? (suena más "correcto"... parece más oop, jeje)

Copio aquí mi código actual antes de tus sugerencias para que los interesados puedan aprender a través de un antiejemplo, jeje:

[code linenumbers=false syntax=gambas]
PUBLIC SUB Form_Open()
DIM aanombre AS String
DIM aacompra AS String
DIM aaventa AS String
DIM aaultimo AS String
DIM loteactual AS NEW lote


DIM xml AS NEW XmlReader

TRY xml.Open(User.Home & "/prueba2/opciones.xml")

IF ERROR THEN 
Message.Error("No se encuentra el archivo o no se puede abrir")
RETURN 
END IF
DO WHILE TRUE
  TRY xml.Read()
  IF xml.Eof THEN BREAK 
  
  IF xml.Node.Type = XmlReaderNodeType.Element
    IF xml.Node.name = "information" 'si es este elemento que no haga nada

    ELSE

    aanombre = xml.Node.Name 'tomo el nombre del elemento
      

      FOR EACH xml.Node.Attributes 
        
        SELECT CASE xml.Node.Name 'esto es muy ineficiente. Tendría que cortar cuando llega a last y pasar a otro elemento
          CASE "bid"
            aacompra = xml.Node.Value 'tomo el valor del elemento
          CASE "ask"
            aaventa = xml.Node.Value
          CASE "last"
            aaultimo = xml.Node.Value
        END SELECT 


      NEXT 

loteactual.nombre = aanombre
loteactual.pcompra = aacompra
loteactual.pventa = aaventa
loteactual.pultimo = aaultimo

variables.almacen.Add(loteactual)
variables.arraybusca.Add(loteactual.nombre)

      ENDIF 
  ENDIF 
  

  IF ERROR THEN Message.Error("no pude leer")
          
LOOP 
TRY xml.Close()
IF ERROR THEN Message.Error("no pude cerrar")
rellenaxml.rellenalbox(ListBox1, "/prueba2/opciones.xml")

END[/code]

Hasta allí las partes del formulario principal.

Ahora el código de la clase lote:

[code linenumbers=false syntax=gambas]PUBLIC nombre AS String
PUBLIC pcompra AS Float
PUBLIC pventa AS Float
PUBLIC pultimo AS Float

PUBLIC SUB leevalores(xmlnombre AS String, xmlpcompra AS Float, xmlpventa AS Float, xmlpultimo AS Float)
  
  nombre = xmlnombre 'aca me mete el nombre del lote
  pcompra = CFloat(xmlpcompra)
  pventa = CFloat(xmlpventa)
  pultimo = CFloat(xmlpultimo)[/code]

END

Y estas dos líneas de un módulo donde almaceno las cosas.

[code linenumbers=false syntax=gambas]PUBLIC almacen AS NEW Object[]
PUBLIC arraybusca AS NEW String[][/code]


En breve voy a poner a rodar tus ideas y postearé los resultados de la prueba.

Muchas gracias!

Saludos.
Santiago.


-----------------------------------
soplo
Martes, 19 Abril 2011, 15:20

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Hola

[quote]La principal diferencia entre tu código y el mío que veo es que instanciamos el objeto de tipo lote en distintos lugares. Vos lo hacés dentro del bucle y después de tener cada valor y yo lo hago al principio de todo... incluso antes de meterme en el bucle... Será eso el problema? [/quote]
Efectivamente. Por eso te lo puse así, para que veas como lo instancio y que luego puedo incluso eliminarlo porque ya no me srive

En cuanto a las variables public yo no estoy en contra de lo que hss hecho. Seguro que te funciona, ahora bien es importante tener claros los conceptos. Por ejemplo si necesitas esas variables públicas lo que te sobra es el método porque no es necesario porque puedes asignarles valores directamente sin necesidad de ese método.

Otra alternativa es que hubieras hecho propiedades. Te explico como hacer la propiedad nombre
[code linenumbers=false syntax=gambas]
private $Nombre as string
property Nombre as string[/code]
Lo que hacemos es crear una variable $Nombre conocida a nivel de clase y no conocida en el exterior.
Luego definimos la propiedad nombre y al darle al enter a esa línea se escribe automáticamente al final de tu clase lo siguiente
[code linenumbers=false syntax=gambas]private function Nombre_Read() as string

end

private sub Nombre_Write(Value as string)

end[/code]
La procedure _Read está para cuando quieras leer el valor de la propiedad nombre allá donde sea
La función _Write está para cuando quieras asignarle un valor a la propiedad nombre.

Todo lo que tu haces es editar esto un poquito para que te quede así
[code linenumbers=false syntax=gambas]private function Nombre_Read() as string
   return $nombre
end

private sub Nombre_Write(Value as string)
   $Nombre=value
end[/code]
Y ya está.
A partir de ese momento tu puedes instanciar una clase y darle valores a la propiedad
Dim L as NEW lotes
[code linenumbers=false syntax=gambas]
'al hacer esto se ejecuta el _write y la variable privada $nombre coge su valor
L.nombre="pepito" [/code]
[code linenumbers=false syntax=gambas]
'al hacer esto se ejecuta el _read y se devuelve lo que la variable privada $nombre valga.
print L.nombre[/code]

Naturalmente es el caso mas simple pero puedes complicarlo cuanto quieras. Por ejemplo tu puedes hacer una propiedad que pida un dni y que cuando se lo das compruebe si es un DNI válido. Para eso en procudure _read añadirías una función ComprobarDni

Si en vez de declarar property pones property read es una propiedad de solo lectura que te devuelve algún valor que tu clase calcula internamente. Por ejemplo yo tengo una clase que hace certificados y le paso una serie de parámetros. La clase calcula el certificado y hay una property read que devuelve ese valor calculado.

Por último decir que los valores que pasas no tienen por que ser integers o strings, etc. Puede ser cualquier cosa. Yo tengo un formulario donde el usuario va escribiendo y rellenando cosas y todo lo que hace se va metiendo en un columnview. Cuando le da a terminar yo instancio una clase y en una propiedad le doy como valor el columnview entero. La clase esa tiene una variable privada de tipo columnview y lo que hago es analizar todo ese columview y en cada caso ir haciendo lo conveniente. Solo es un ejemplo de que puedes hacer una propiedad que como parámetro tenga un columnview, un formulario, otra clase o cualquier cosa que se te pueda ocurrir.


-----------------------------------
santijav
Martes, 19 Abril 2011, 15:33

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Sublime...

Hombre... me imagino que sabes programar muy bien... pero en lo que no tengo absolutamente ninguna duda es en tu pedagogía y en tu capacidad para explicar las cosas.

Simplemente... muchas gracias!


-----------------------------------
santijav
Miercoles, 20 Abril 2011, 16:02

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Soplo,

En el código que comentaste originariamente ponías un

L.delete (volviendo al tema: tienes un objeto de clase lote, que lo has almacenado en un object[] y ahora lo eliminas)

Pues bien... yo en el objeto que creé no puse el método delete, y por defecto no lo tengo.

Qué método (función/procedure) tengo que agregar y con qué código para poder destriuir los objetos cuando ya no los uso más?

Muchas gracias!


-----------------------------------
soplo
Jueves, 21 Abril 2011, 05:14

Re: Consulta Acerca De Una Matriz De Objetos: Todos Los Objetos Tienen Los Mismos Datos¿?
-----------------------------------
Efectivamente delete no está para clases, solo está para objetos.
Osea que si tu haces
Dim T as NEW textbox
Luego puedes hacer T.Delete

Pero si lo que haces es 
Dim T as NEW Lotes
luego no puedes hacer T.Delete porque T es una clase no un control. Eso te lo puse mal.

Esa clase se destruirá al terminar la función o procedure en la que tiene ámbito.

En cualquier caso creo que queda clara la solución a tu problema
 :mryellow:


