Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 01-12-2003
Avatar de sarackganda
sarackganda sarackganda is offline
Miembro
 
Registrado: may 2003
Posts: 28
Poder: 0
sarackganda Va por buen camino
¿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;
En la implementación del TMiGrid.DrawCell quiero llamar al método heredado pero no de TStringGrid, de TDrawGrid.


Vale aclarar que solucioné mi problema sin hacer esto, pero es que me quedó la duda ...

Chagracia!
__________________
Sarackgand in the workshop wrassaraba!
Responder Con Cita
  #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
Poder: 29
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
  #3  
Antiguo 05-12-2003
Avatar de marto
marto marto is offline
Miembro
 
Registrado: may 2003
Ubicación: Barcelona, Catalunya
Posts: 882
Poder: 21
marto Va por buen camino
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
Responder Con Cita
  #4  
Antiguo 05-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
Poder: 29
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!

Con respecto a:
Cita:
marto comentó:
...tendría que ser más sencillo que todo eso. ¿No llegaría con hacer un cast de Self a la clase desada?...
Debo decir que si bastaría un simple moldeo de tipo (type cast) si el método en cuestión fuera estático. Pero debido a que se trata de un método virtual, el molde de tipo no hace la llamada al método deseado.

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 .
Responder Con Cita
  #5  
Antiguo 15-12-2003
Avatar de sarackganda
sarackganda sarackganda is offline
Miembro
 
Registrado: may 2003
Posts: 28
Poder: 0
sarackganda Va por buen camino
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!
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 01:19:13.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi