FTP | CCD | Buscar | Trucos | Trabajo | Foros |
|
Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
|
Herramientas | Buscar en Tema | Desplegado |
#1
|
||||
|
||||
¿Cómo puedo llamar al método heredado de una clase que no es el ancestro directo?
El tema es así, tengo un componente heredado de TStringGrid y necesito llamar al método DrawCell pero de la clase TDrawGrid.
Tengamos en cuenta que la jerarquía es la siguiente Código:
TDrawGrid = class(TCustomGrid) protected procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override; end; TStringGrid = class(TDrawGrid) protected procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override; end; TMiGrid = class(TStringGrid) protected procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override; end; Vale aclarar que solucioné mi problema sin hacer esto, pero es que me quedó la duda ... Chagracia!
__________________
Sarackgand in the workshop wrassaraba! |
#2
|
||||
|
||||
¡Buen día a todos!
Sarackganda: Conozco una forma de llamar a un método heredado por una clase ancestral, que ha sido escondido por otra clase ancestral más inmediata. Como sucede en este caso, donde el método virtual DrawCell de TDrawGrid es escondido por le método redefinido DrawCell de TStringGrid. La clave para lograrlo es cambiar temporalmente la clase del objeto. Aquí un ejemplo de ello: Código:
... Type T1 = Class Procedure P1; Virtual; Procedure P2; Virtual; End; T2 = Class (T1) Procedure P1; Override; End; T3 = Class (T2) Procedure P2; Override; Procedure Llamador1; Procedure Llamador2; End; Procedure T1.P1; Begin ShowMessage ('Se llamó a T1.P1'); P2; End; Procedure T1.P2; Begin ShowMessage ('Se llamó a T1.P2'); End; Procedure T2.P1; Begin ShowMessage ('Se llamó a T2.P1'); End; Procedure T3.P2; Begin ShowMessage ('Se llamó a T3.P2'); End; { Cambia temporalmente la clase del objeto para llamar al método T1.P1 } Procedure T3.Llamador1; Begin { Cambiar la clase del objeto a T1 } TClass (Pointer (Self)^) := T1; Try ShowMessage ('Se cambió la clase del objeto a ' + ClassName); P1; { Con el cambio de clase esta sentencia llama a T1.P1, sin embargo T1.P1 llama a T1.P2 } Finally TClass (Pointer (Self)^) := T3; { Restauración de la clase original } ShowMessage ('Se restauró la clase del objeto a ' + ClassName); End; End; { Cambia temporalmente la clase del objeto para obtener la dirección del método T1.P1. Luego lo llama. } Procedure T3.Llamador2; Type TMetodo = Procedure Of Object; Var Metodo :TMetodo; Begin { Cambiar la clase del objeto a T1 } TClass (Pointer (Self)^) := T1; Try ShowMessage ('Se cambió la clase del objeto a ' + ClassName); Metodo := P1; { Obtención de la dirección de memoria del método T1.P1 } Finally TClass (Pointer (Self)^) := T3; { Restauración de la clase original } ShowMessage ('Se restauró la clase del objeto a ' + ClassName); End; { Ejecución del método T1.P1. Esta sentencia llama T1.P1, pero T1.P1 llama al metodo redefinido T3.P2. } Metodo; End; ... Begin With T3.Create Do Try ShowMessage ('Solución 1...'); Llamador1; ShowMessage ('Solución 2...'); Llamador2; Finally Free; End; ... Llamador2 cambia la clase del objeto por T1, para obtener la dirección de memoria del método T1.P1, pero antes de llamarlo restaura la clase del objeto a T3. Durante la llamada a T1.P1 el objeto es de clase T3, por lo que T1.P1 podrá acceder a elementos de T3 (T1.P1 llama al método redefinido T3.P2). En la mayoría de los casos aconsejo utilizar la segunda solución (Llamador2), ya que se mantiene más la integridad del objeto. En este caso, es importante que el tipo de dato auxiliar TMetodo tenga una declaración equivalente al método en cuestión, en cuanto a cantidad, posición y tipo de parámetros, así como tipo de valor devuelto y la convención de llamada. El cambio de clase es bastante fácil, gracias a que ésta se guarda en los primeros cuatro bytes de la instancia del objeto, es decir en Código:
Pointer (Self)^ Espero esto sea de utilidad. Seguimos en contacto. Al González . |
#3
|
||||
|
||||
Holap,
No lo he probado, pero si no se me escapa nada tendría que ser más sencillo que todo eso. ¿No llegaría con hacer un cast de Self a la clase desada? Código:
Procedure T3.P1; Begin T1(Self).P1; End; O me estoy dejando algo?
__________________
E pur si muove |
#4
|
||||
|
||||
¡Buen día a todos!
Con respecto a: Cita:
De hecho, el ejemplo que propones generaría un error de desbordamiento de pila por recursión infinita, en caso de que T3 también tuviera redefinido el método virtual P1. O en el mejor de los casos, se estaría llamando al método redefinido T2.P1, pero no al método T1.P1 deseado. Esto se debe a que las llamadas a métodos virtuales son enlazadas en tiempo de ejecución, dependiendo de la clase real del objeto (los primeros cuatro bytes del bloque de memoria que ocupa). Por lo que un molde de tipo en tiempo de compilación no tiene efecto al respecto. Espero esto sea de utilidad. Seguimos en contacto. Al González . |
#5
|
||||
|
||||
Muchas gracias, sus respuestas fueron muy interesantes, aunque mi pregunta apuntaba a alguna herramienta propia del lenguage, pero por lo visto, estas son las soluciones.
Chagracia!
__________________
Sarackgand in the workshop wrassaraba! |
|
|
|