2010-10-28 12 views
10

Necesito reparar un componente de un tercero. La clase de este componente tiene variable privada que se utiliza activamente por sus descendientes:Delphi: escribir en el campo del antecesor privado en la clase descendiente

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; 

Esto funciona porque ambas clases están en la misma unidad, por lo que Usted es un poco "amigos".

Pero si voy a tratar de crear una nueva clase en una nueva unidad

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

No puedo acceder a FSomeVar más, pero necesito usarlo para mi dosis. Y realmente no quiero reproducir en mi código todo ese árbol de clases base.

¿Puede aconsejar algún truco rápido para acceder a ese campo privado sin cambiar la unidad del componente original si es posible?

Respuesta

5

Tienes que utilizar un truco para acceder a un campo privado en cualquier clase (incluida una clase base) en una unidad diferente. En su caso definir en su unidad:

type 
    __TThirdPartyComponentBase = class 
    private 
    FSomeVar: Integer; 
    end; 

continuación, obtener el acceso:

__TThirdPartyComponentBase(Self).FSomeVar := 123; 

Por supuesto, eso es peligroso, ya que necesitará para controlar los cambios en la clase base. Porque si se cambia el diseño de los campos y se perderá este hecho, entonces el enfoque anterior dará lugar a fallas, AV's, etc.

+2

@Andrew: tenga en cuenta que esta solución se interrumpirá tan pronto como el diseño de la memoria del componente ancestro (de terceros) cambie. Es posible que no notes que se rompe, ya que nada te lo advertirá. O es posible que vea un comportamiento espurio incorrecto (si tiene suerte: violaciones de acceso) porque comienza a sobrescribir datos que no son suyos. –

+0

@Jeroen Pluimers He notado a Andrew sobre ese hecho. Pero no hay otras soluciones para este problema. – oodesigner

+0

Los ayudantes de clase pueden hacer esto sin piratear, ver mi respuesta :) –

-1

Expone el valor de la variable privada por una propiedad protegida en 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; 

En TMyFixedComponent clase de uso de la propiedad MyVar en el procedimiento que le gustaría anular.

+5

Pero esto cambiará el código original de TThirdPartyComponent. Quería alguna solución sin volver a escribir el código de TThirdPartyComponent ni cambiar la unidad del componente original. – Andrew

0

No sé si esto ayudará, pero parece recordar que hay una manera para "descifrar" una variable privada en visibilidad.

Sé, por ejemplo, que he encontrado advertencias del compilador cuando moví una propiedad de menor visibilidad (en la clase base) a un nivel más visible (en mi descendiente). La advertencia indica que se está declarando con un nivel de visibilidad diferente ...

Ha pasado un tiempo y no estoy seguro, pero creo que lo que puede hacer es declarar en su descendiente la misma variable que protegida. (Puede que tenga que usar la palabra clave Redeclare para compilar esto.)

Lo siento, no tengo información más específica sobre cómo hacer esto (si es posible). Quizás esta publicación le pregunte a uno de los asistentes aquí en corregirme! :-)

18

Mediante el uso de class helpers es posible lograr el acceso a las partes privadas de la clase base de la clase derivada sin perder la seguridad del tipo.

Sólo añadir estas declaraciones en otra unidad:

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. 

Como puede verse a partir de los comentarios en el código, FSomeVar está expuesto por un ayudante de clase de la clase TThirdPartyComponentBase. Otro asistente de clase para el TThirdPartyComponent implementa el procedimiento Foo. Allí, el acceso a la propiedad SomeVar del asistente de clase base se realiza mediante un tipo de conversión a la clase base.

+1

¡Muy bonito! Me gusta. –

+3

Tenga en cuenta que los ayudantes de clase en otra unidad ya no pueden acceder a miembros privados a partir de Delphi 10.1 Berlin, ya que Embarcadero lo consideró un error. Ver http://stackoverflow.com/questions/9410485/how-do-i-use-class-helpers-to-access-strict-private-members-of-a-class#comment61026976_9410717 para más detalles –

Cuestiones relacionadas