¡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;
...
Llamador1 cambia la clase del objeto, para poder llamar al método T1.P1, y luego restaura la clase a T3. Durante esa llamada a T1.P1 el objeto es de clase T1, por lo que no hay acceso a nada de T3 (T1.P1 llama al método T1.P2);
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
, según el ejemplo. Sin embargo esta técnica debe emplearse con cuidado, ya que un cambio de clase equivocado o que no sea restaurado, puede generar errores de acceso de memoria y mal comportamiento de los objetos.
Espero esto sea de utilidad. Seguimos en contacto.
Al González
.