Ver Mensaje Individual
  #2  
Antiguo 04-12-2003
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Reputación: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
¡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
Código:
Pointer (Self)^
, 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 .
Responder Con Cita