2011-01-11 24 views
22

Estoy anulando un método virtual, y quiero llamar al heredado. Pero no quiero llamar al antecesor inmediato , quiero llamar al antes de.Delphi: ¿Cómo llamar antecesor heredado heredado en un método virtual?

TObject 
    TDatabaseObject 
     TADODatabaseObject <---call this guy 
     TCustomer  <---skip this guy 
      TVIP   <---from this guy 

He intentado fundición mi self como el antepasado, y llame al método de eso, sino que dio lugar a desbordamiento de pila recursiva:

procedure TVip.SetProperties(doc: IXMLDOMDocument); 
begin 
    TADODatabaseObject(Self).SetProperties(doc); //skip over TCustomer ancestor 
    ... 
end; 

He intentado añadir la palabra clave inherited, pero eso no lo hace compilar:

procedure TVip.SetProperties(doc: IXMLDOMDocument); 
begin 
    inherited TADODatabaseObject(Self).SetProperties(doc); //skip over TCustomer ancestor 
    ... 
end; 

Posible?

+8

@Ian creo que las campanas de alarma debe sonar para usted ahora! El diseño de esta parte de tu sistema no puede ser correcto. –

+0

@David Heffernan Y tienes razón. Pero pretendo que el antepasado es 'TListView', realmente no puedo rediseñar una clase sobre la que no tengo control. –

+0

@Ian ¿En qué punto tienes el control? TADODatabaseObject? TCustomer? –

Respuesta

16

No puede hacerlo en un idioma normal, ya que esto rompería los aspectos orientados a objetos del idioma.

Puede jugar con punteros y moldes inteligentes para hacer esto, pero antes incluso de comenzar a responder eso: ¿esto es realmente lo que quiere?

Como otros mencionar:. Su necesidad suena como una grave "olor de diseño" (que es similar a code smell, pero más severa

Editar:

Bajando el puntero jugueteando carretera podría ahorrar trabajar en el corto plazo, y le puede costar semanas de trabajo en el largo plazo
Esto hace que para una buena lectura en la que:.. Upstream decisions, downstream costs

+1

+1 por olor al diseño –

+1

Aceptado por "no se puede hacer" –

+2

Es interesante observar que puede obtener una recursión sin fin (tiempo de desbordamiento de la pila) si trata de "saltar la jerarquía de clases" y está invocando una virtual método. –

3

Si realmente desea hacer esto, debe extraer en un método protegido separado la parte de la jerarquía de herencia a la que desea poder hacer referencia directamente. Esto le permitirá llamar desde cualquier lugar sin que el envío de métodos virtuales lo derrote.

Sin embargo, como he comentado, parece que hay algo mal con su diseño de clase.

+0

Para ambos: algo está apagado con el diseño. Pero el antepasado está enlatado y funcionando. probablemente terminaré destruyéndolo por completo, introduciendo muchos errores en el proceso. Esperaba que esta única línea de código me ahorrara unos días de trabajo. –

+0

Tuve un problema similar. Las clases base son 30,000 líneas de código de componente de terceros. –

27

usted puede hacerlo usando un truco de obtener direcciones estáticas del método virtual:

type 
    TBase = class 
    procedure Foo; virtual; 
    end; 

    TAnsestor = class(TBase) 
    procedure Foo; override; 
    end; 

    TChild = class(TAnsestor) 
    procedure Foo; override; 
    procedure BaseFoo; 
    end; 

procedure TBase.Foo; 
begin 
    ShowMessage('TBase'); 
end; 

procedure TAnsestor.Foo; 
begin 
    ShowMessage('TAnsestor'); 
end; 

procedure TChild.Foo; 
begin 
    ShowMessage('TChild'); 
end; 

type 
    TFoo = procedure of object; 

procedure TChild.BaseFoo; 
var 
    Proc: TFoo; 

begin 
    TMethod(Proc).Code := @TBase.Foo; // Static address 
    TMethod(Proc).Data := Self; 
    Proc(); 
end; 

procedure TForm4.Button1Click(Sender: TObject); 
var 
    Obj: TChild; 
    Proc: TFoo; 

begin 
    Obj:= TChild.Create; 
    Obj.BaseFoo; 
// or else 
    TMethod(Proc).Code := @TBase.Foo; // Static address 
    TMethod(Proc).Data := Obj; 
    Proc(); 

    Obj.Free; 
end; 
+2

+1 Si realmente quiere hacer esto, entonces que así sea !! –

+0

Este es un gran truco, lo usé en este escenario - I utilizar un marco de terceros, el método heredado hace algo incorrecto: come excepciones que quiero capturar, así que necesito saltearlas, volver a implementar una lógica ligeramente modificada de eso, luego llamar al método "abuelo" (TBase) . –

7

recuerdo que tenía que hacer algo como esto hace algunos años trabajando en torno a algunos limitación de diseño de la jerarquía VCL.

por lo que parece que fue algo como esto:

type 
    TGrandParent = class(TObject) 
    public 
    procedure Show;virtual; 
    end; 

    TParent = class(TGrandParent) 
    public 
    procedure Show;override; 
    end; 

    THackParent = class(TGrandParent) 
    private 
    procedure CallInheritedShow; 
    end; 

    TMyObject = class(TParent) 
    public 
    procedure Show;override; 
    end; 


{ TGrandParent } 

procedure TGrandParent.Show; 
begin 
    MessageDlg('I''m the grandparent', mtInformation, [mbOk], 0); 
end; 

{ TParent } 

procedure TParent.Show; 
begin 
    inherited; 
    MessageDlg('I''m the parent', mtInformation, [mbOk], 0); 
end; 

{ THackParent } 

procedure THackParent.CallInheritedShow; 
begin 
    inherited Show; 
end; 

{ TVIP } 

procedure TMyObject.Show; 
begin 
    THackParent(Self).CallInheritedShow; 
end; 

procedure TForm6.Button6Click(Sender: TObject); 
var 
    VIP: TMyObject; 
begin 
    VIP:=TMyObject.Create; 
    try 
    VIP.Show; 
    finally 
    VIP.Free; 
    end; 
end; 

No es la cena-elegante pero sigue siendo una solución :)

Cuestiones relacionadas