2010-10-28 13 views
10

Ho bisogno di riparare un componente di terze parti. classe di questo componente ha variabile privata che è attivamente utilizzato dai suoi discendenti:Delphi: scrivere nel campo degli antenati privati ​​in classe discendente

TThirdPartyComponentBase = class 
private 
    FSomeVar: Integer; 
public 
    ... 
end; 

TThirdPartyComponent = class (TThirdPartyComponentBase) 
protected 
    procedure Foo; virtual; 
end; 

procedure TThirdPartyComponent.Foo; 
begin 
    FSomeVar := 1; // ACCESSING PRIVATE FIELD! 
end; 

Questo funziona perché entrambe le classi sono nella stessa unità, quindi sono Kinda "amici".

Ma se cercherò di creare una nuova classe in una nuova unità

TMyFixedComponent = class (TThirdPartyComponent) 
    procedure Foo; override; 
end; 

Non riesco ad accedere più FSomeVar, ma ho bisogno di usarlo per il mio fix. E davvero non voglio riprodurre nel mio codice tutto quell'albero delle classi base.

Potete consigliare qualche trucco rapido per accedere a quel campo privato senza cambiare l'unità del componente originale se è possibile a tutti?

risposta

5

È necessario utilizzare un hack per accedere a un campo privato in qualsiasi classe (inclusa una classe base) in un'unità diversa. Nel tuo caso definire nella vostra unità:

type 
    __TThirdPartyComponentBase = class 
    private 
    FSomeVar: Integer; 
    end; 

quindi ottenere l'accesso:

__TThirdPartyComponentBase(Self).FSomeVar := 123; 

Naturalmente, questo è pericoloso, perché è necessario controllare i cambiamenti nella classe base. Perché se il layout dei campi verrà modificato e mancherà questo fatto, l'approccio sopra riportato porterà a guasti, AV, ecc.

+2

@Andrew: si noti che questa soluzione si interromperà non appena cambia il layout della memoria del componente antenato (di terze parti). Potresti non notare che si rompe, poiché nulla ti avviserà di ciò. Oppure potresti vedere un comportamento errato errato (se sei fortunato: accedere a violazioni) perché inizi a sovrascrivere i dati che non sono tuoi. –

+0

@Jeroen Pluimers Ho notato Andrew su questo fatto. Ma non ci sono altre soluzioni per questo problema. – oodesigner

+0

Gli helper di classe possono eseguire questa operazione senza hacking, vedere la mia risposta :) –

-1

Esporre il valore della variabile privata da una proprietà protetta in TThirdPartyComponent.

TThirdPartyComponent = class (TThirdPartyComponentBase) 
private 
    Procedure SetValue(Value: Integer); 
    Function GetValue: Integer; 
protected 
    Property MyVar: Integer read GetValue write Setvalue; 
    procedure Foo; virtual; 
end; 

Procedure TThirdPartyComponent.SetValue(Value: Integer); 
begin 
    FSomeVar := Value ; 
end; 

Function GetValue: Integer; 
begin 
    result := FSomeVar; 
end; 

In TMyFixedComponent classe utilizzare il MyVar proprietà nella procedura che si desidera sovrascrivere.

+5

Ma questo cambierà il codice originale di TThirdPartyComponent. Volevo qualche soluzione senza riscrivere il codice di TThirdPartyComponent o cambiare l'unità del componente originale. – Andrew

0

Non so se questo aiuterà, ma mi sembra di ricordare che c'è un modo "crack" una variabile privata in visibilità.

Per esempio, ho riscontrato degli avvisi dal compilatore quando ho spostato una proprietà dalla visibilità inferiore (nella classe base) a un livello più visibile (nella mia discendente). L'avvertimento ha dichiarato che è stato dichiarato a un diverso livello di visibilità ...

È passato un po 'di tempo e non ne sono sicuro, ma credo che ciò che si può fare è nel discendente dichiarare la stessa variabile come protetta. (Potrebbe essere necessario utilizzare la parola chiave Redeclare per compilare.)

Spiacente, non ho informazioni più specifiche su come eseguire questa operazione (se è effettivamente possibile.) Forse questo post richiederà uno dei wizard qui nel correggermi! :-)

18

Con l'utilizzo di class helpers è possibile accedere alle parti private della classe di base dalla classe derivata senza perdere la sicurezza del tipo.

Basta aggiungere queste dichiarazioni in un'altra unità:

Uses YourThirdPartyComponent; 

type 
    // A helper to the base class to expose FSomeVar 
    TMyBaseHelper = class helper for TThirdPartyComponentBase 
    private 
    procedure SetSomeVar(value : integer); 
    function GetSomeVar: integer; 
    public 
    property SomeVar:integer read GetSomeVar write SetSomeVar; 
    end; 

    TMyFixedComponent = class helper for TThirdPartyComponent 
    protected 
    procedure Foo; 
    end; 

procedure TMyFixedComponent.Foo; 
begin 
    // Cast to base class and by the class helper TMyBaseHelper the access is resolved 
    TThirdPartyComponentBase(Self).SomeVar := 1; 
end; 

function TMyBaseHelper.GetSomeVar: integer; 
begin 
    Result := Self.FSomeVar; // ACCESSING PRIVATE FIELD! 
end; 

procedure TMyBaseHelper.SetSomeVar(value: integer); 
begin 
    Self.FSomeVar := value; // ACCESSING PRIVATE FIELD! 
end; 

// Testing 
var 
    TSV: TThirdPartyComponent; 
begin 
    TSV := TThirdPartyComponent.Create; 
    try 
    TSV.Foo;  
    WriteLn(IntToStr(TSV.SomeVar)); // Writes 1 
    finally 
    TSV.Free; 
    end; 
end. 

Come si può vedere dai commenti nel codice, FSomeVar è esposta da un helper classe dalla classe TThirdPartyComponentBase. Un altro helper di classe per lo TThirdPartyComponent implementa la procedura Foo. Qui, l'accesso alla proprietà SomeVar dell'helper della classe base viene effettuato tramite un cast di tipo sulla classe base.

+1

Molto bello! Mi piace. –

+3

Si noti che gli helper di classe in un'altra unità non possono più accedere ai membri privati ​​a partire da Delphi 10.1 Berlin, perché questo è stato considerato un bug di Embarcadero. Vedi http://stackoverflow.com/questions/9410485/how-do-i-use-class-helpers-to-access-strict-private-members-of-a-class#comment61026976_9410717 per ulteriori dettagli –

Problemi correlati