Hola Juan! Muchas gracias por el aporte, yo también creo que es un *bug*, aunque te repito no se me ocurre un caso de uso. Si lo que buscas es código seguro entre desarrolladores se me ocurre que podes dejar el access y assign en la clase *Persona,* pero lanzar una excepción en el access y hacer la verdadera implementación en el access de la clase *Empleado*. ¿Se entiende? Con esto te cubrís de que si algún desarrollador quiere obtener esa propiedad desde *Persona* le va a informar que no puede, con esto más el uso de pruebas unitarias estarías cubierto [?]
Saludos, César PD: Me alegro que uses pruebas unitarias!! El 2 de enero de 2015, 12:38, Fernando D. Bozzo <[email protected]> escribió: > Hola Juan: > > Muy buen ejemplo. Nunca me había pasado, supongo que porque nunca separé > la creación de los eventos access y assign. > > Igualmente, si tu primer ejemplo "bueno" lo ejecutás más de una vez, > también da error, ya que el bug tiene que ver con la carga y cacheo de la > definición de las clases en memoria, donde aparentemente solapa ambas > definiciones y por eso espera un "access" donde no existe. > > Creo que la mejor solución es crear ambos eventos en la misma clase, así > no dependés de estar alterando la carga de una clase y otra, aunque tampoco > hice la prueba para constatarlo. > > > Saludos! > > > El 2 de enero de 2015, 15:35, Juan R. Rossano <[email protected]> > escribió: > >> Gracias a todos. Conceptualmente estoy de acuerdo en que es mejor por >> norma definir los metodos en la clase que declara la propiedad. El problema >> se dio porque empezo a dar error un test unitario y de ahi empezamos a >> analizar que lo habia generado; entonces detectamos esta situación. Es >> mejor por norma que si hay un _access y un _assign se definan en la clase >> que declara la variable, pero en MSDN especifica que son metodos >> independientes y puede existir uno sin otro, no menciona ni controla que >> ese metodo no se defina en la subclase y funciona con esa definicion. >> Adjunto (y copio al pie por si no se puede descargar) dos PRG con el >> ejemplo que mencione (que no es el real sino uno creado para probar este >> caso); uno es EjemploMAL.prg y otro EjemploBIEN.prg que son simplemente el >> que da error y el que funciona bien, su unica diferencia es el orden de >> creacion de los objetos: primero persona y despues empleado no da error, >> primero empleado y despues persona si da error (lineas 5 y 6). Incluso >> ejecutando primero el que esta bien y despues el que esta mal funciona, a >> veces si primero se ejecuta el que esta mal y despues el que esta bien >> sigue dando error aun en el que esta bien. >> Mi deduccion por lo que investigue es que al crear un objeto persona se >> puede usar la funcion amembers() para acceder a la clase base y supongo >> (pero no pude encontrar ninguna referencia) que carga toda la estructura >> jerarquica de herencia. En el caso original nuestro devuelve 85 propiedades >> en la clase base y 89 en la implementada. Y como el _access tiene >> prevalencia sobre la variable creo que deja la referencia y cuando se crea >> el segundo objeto (ahora de la clase base) ya detecta que esta cargada la >> estructura, no vuelve a cargarla y toma esa referencia a un _access que no >> existe, porque el error es que *no se encontro la propiedad >> variable_access*. Y eso si creo que es un Bug ya que no deberia >> funcionar dependiendo del orden de creacion de los objetos. >> Comparti el caso para confirmarlo porque no encontramos documentacion, es >> un caso de deduccion personal y toda opinion aporta. Creo que sirve como >> norma de codificacion .... >> Gracias a todos. >> >> *Juan* >> >> Ejemplo Bien >> >> local loEmp, loPer, lnCantidad >> dimension laPropiedades(1) >> >> *!* Orden de creacion de objetos >> loPer = newobject( 'Persona' ) >> loEmp = newobject( 'Empleado' ) >> *!* Fin de Orden de creacion de objetos >> >> ?loPer.Apellido >> lnCantidad = Amembers( laPropiedades,loPer,0,"UG+" ) >> ?lnCantidad >> >> for i = 1 to lnCantidad >> lcPropiedad = "loPer."+ alltrim( laPropiedades[i] ) >> if vartype( evaluate( lcPropiedad ) ) = "O" >> if pemstatus(&lcPropiedad,"release",5) >> lcEliminaReferencia = lcPropiedad + ".release" >> &lcEliminaReferencia >> endif >> endif >> endfor >> >> loPer.Apellido = 'GOMEZ' >> >> ?loPer.Apellido >> >> loPer = null >> >> loEmp = newobject( 'Empleado' ) >> ?loEmp.Apellido >> lnCantidad = Amembers( laPropiedades,loEmp,0,"UG+" ) >> ?lnCantidad >> >> lnCantidad = Amembers( laPropiedades,'Persona',0,"UG+" ) >> ?lnCantidad >> >> loEmp = null >> >> return >> >> >> define class Persona as Custom >> >> Apellido = '' >> Nombre = '' >> >> >> *----------------------------------------------------------------------------------------- >> function Apellido_assign( tcVal as String) as Void >> this.Apellido = tcVal >> endfunc >> >> >> enddefine >> >> define class Empleado as Persona >> >> >> documento = 0 >> >> >> *----------------------------------------------------------------------------------------- >> function Apellido_access() as String >> return this.Apellido >> endfunc >> >> enddefine >> >> >> >> Ejemplo Mal >> >> local loEmp, loPer, lnCantidad >> dimension laPropiedades(1) >> >> *!* Orden de creacion de objetos >> loEmp = newobject( 'Empleado' ) >> loPer = newobject( 'Persona' ) >> *!* Fin de Orden de creacion de objetos >> >> ?loPer.Apellido >> lnCantidad = Amembers( laPropiedades,loPer,0,"UG+" ) >> ?lnCantidad >> >> for i = 1 to lnCantidad >> lcPropiedad = "loPer."+ alltrim( laPropiedades[i] ) >> if vartype( evaluate( lcPropiedad ) ) = "O" >> if pemstatus(&lcPropiedad,"release",5) >> lcEliminaReferencia = lcPropiedad + ".release" >> &lcEliminaReferencia >> endif >> endif >> endfor >> >> loPer.Apellido = 'GOMEZ' >> >> ?loPer.Apellido >> >> loPer = null >> >> loEmp = newobject( 'Empleado' ) >> ?loEmp.Apellido >> lnCantidad = Amembers( laPropiedades,loEmp,0,"UG+" ) >> ?lnCantidad >> >> lnCantidad = Amembers( laPropiedades,'Persona',0,"UG+" ) >> ?lnCantidad >> >> loEmp = null >> >> return >> >> >> define class Persona as Custom >> >> Apellido = '' >> Nombre = '' >> >> >> *----------------------------------------------------------------------------------------- >> function Apellido_assign( tcVal as String) as Void >> this.Apellido = tcVal >> endfunc >> >> >> enddefine >> >> define class Empleado as Persona >> >> >> documento = 0 >> >> >> *----------------------------------------------------------------------------------------- >> function Apellido_access() as String >> return this.Apellido >> endfunc >> >> enddefine >> >> >> >> El 01/01/2015 11:25 p.m., César Pistiner escribió: >> >> Buenas noches Juan >> >> Yo estoy de acuerdo con Omar, acces y assign van juntos o simplemente >> usas uno u el otro, pero siempre dentro de la misma clase. De todas formas >> es interesante el comportamiento que mencionas de vfp. Si instancias una >> clase primero que la otra funcionan distinto? Eso si esta feo... Igual te >> repito nunca se me dio la necesidad de tener algo así, cuál es tu caso de >> uso? >> >> Saludos, >> César >> >> El jue, ene 1, 2015 04:04 PM, Fernando D. Bozzo <[email protected]> >> escribió: >> >>> Omar tiene razón: >>> >>> Access y Assign conviene crearlos juntos y simplemente son usados por >>> quienes lo necesiten. >>> >>> Saludos.- >>> >>> >>> >>> El 1 de enero de 2015, 19:58, Omar Bellio <[email protected]> >>> escribió: >>> >>> A ver si entendí bien: >>>> >>>> Cuando vos definís una clase con una propiedad, todo eso hereda hacia >>>> abajo, Empleado hereda de Persona, si vos ponés un método en Empleado, >>>> Persona no lo va a ver. Normalmente cuando pasa eso, se pone una interfaz >>>> (creo que se llama así) en la clase madre, es decir, definís el método y no >>>> le ponés código, queda abstracto para especializar en las subclases. De ese >>>> modo, el método siempre está, aunque no haga nada. >>>> >>>> >>>> >>>> Resumiendo para tu ejemplo: el método _Access tiene que estar en la >>>> clase madre, para que no te pase eso. >>>> >>>> >>>> >>>> Saludos. >>>> >>>> >>>> >>>> *De:* [email protected] [mailto:[email protected]] *En nombre de *Jose Paez >>>> *Enviado el:* miércoles, 31 de diciembre de 2014 07:59 p.m. >>>> *Para:* GUFA List Member >>>> *Asunto:* [GUFA] _Access y _Assign >>>> >>>> >>>> >>>> Hola Juan >>>> >>>> >>>> >>>> Yo he utilizado bastante estos métodos en un framework que desarrolle >>>> hace mucho tiempo. >>>> >>>> Tendrás un ejemplo que puedas pasarnos (.prg o .vcx) para analizarlo. >>>> >>>> >>>> >>>> Saludos >>>> >>>> >>>> >>>> José Paez >>>> >>>> >>>> >>>> >>>> >>>> > Date: Wed, 31 Dec 2014 10:32:05 -0300 >>>> > From: [email protected] >>>> > To: [email protected] >>>> > Subject: [GUFA] _Access y _Assign >>>> > >>>> > Descubrimos un problema con los metodos _Access y _Assign en >>>> herencia. >>>> > En una clase hay una propiedad y esta tiene definido el metodo >>>> _assign. >>>> > Otra clase hereda de esta y define para esa propiedad el metodo >>>> _access. >>>> > Pongo como ejemplo una clase Persona y otra Empleado que hereda de >>>> > Person. Se crea un objeto de cada clase. Si se crea primer la clase >>>> base >>>> > (persona) y despues la subclase (empleado) se puede acceder a la >>>> > propiedad de la primera (ejemplo Persona.Apellido). Si se crea >>>> primero >>>> > la subcase (empleado) y despues la base (persona) y se quiere acceder >>>> a >>>> > la propiedad que tiene el metodo _access en la subclase da error >>>> porque >>>> > no encuenta el metodo _access (al intentar Persona.Apellido da error >>>> > porque no encuentra apellido_access). Por lo que estuve investigando >>>> el >>>> > tema es que cuando crea un objeto levanta la estructura jerarquica de >>>> la >>>> > clase (a la que se puede acceder con la funcion amembers() >>>> consultando >>>> > por el objeto o cada clase en la jerarquia) y hace referencia a cada >>>> > propiedad si tiene algunos de los metodos (_access o _assign). No >>>> > encontre documentación que lo mencione, solo paso y lo confirme con >>>> un >>>> > pequeño ejemplo como el que describi (una clase persona con >>>> propiedades >>>> > apellido y nombre y una funcion apellido_assign y otra clase empleado >>>> > que hereda de persona con una propiedad mas cuil y el metodo >>>> > apellido_access). En la documentacion de microsoft menciona >>>> > explicitamente que se puede usar indistintamente _access o _assign, >>>> pero >>>> > nada habla de la herencia ¿alguien sabe algo de esto? Por lo que >>>> vimos >>>> > si se usa _access o _assign hay que crearlas en la misma definicion >>>> > donde esta la propiedad a menos que sea una clase abstracta y no se >>>> cree >>>> > ninguna instancia de la misma. No se si interesa pero para nosotros >>>> es >>>> > importante como norma de desarrollo. >>>> > Muchas gracias. >>>> > >>>> > >>>> >>> >> >
