Delphi: запись в поле частного предка в классе потомков - PullRequest
10 голосов
/ 28 октября 2010

Мне нужно починить сторонний компонент.Класс этого компонента имеет закрытую переменную, которая активно используется его потомками:

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; 

Это работает, потому что оба класса находятся в одном модуле, поэтому они вроде как "друзья".

Ноесли я попытаюсь создать новый класс в новом модуле

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

, я больше не смогу получить доступ к FSomeVar, но мне нужно использовать его для исправления.И я действительно не хочу воспроизводить в своем коде все это дерево базовых классов.

Можете ли вы посоветовать какой-нибудь быстрый взлом для доступа к этому приватному полю без изменения единицы исходного компонента , если это вообще возможно?

Ответы [ 4 ]

18 голосов
/ 08 июля 2012

Используя class helpers, можно получить доступ к закрытым частям базового класса из производного класса без потери безопасности типов.

Просто добавьте эти объявления в другой модуль:

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.

Как видно из комментариев в коде, FSomeVar предоставляется помощником класса из класса TThirdPartyComponentBase.Другой помощник класса для TThirdPartyComponent реализует процедуру Foo.Там доступ к свойству SomeVar помощника базового класса осуществляется с помощью приведения типа к базовому классу.

5 голосов
/ 28 октября 2010

Вы должны использовать хак для доступа к закрытому полю в любом классе (включая базовый класс) в другом модуле. В вашем случае определите в своем подразделении:

type
  __TThirdPartyComponentBase = class 
  private 
    FSomeVar: Integer;
  end;

Тогда получите доступ:

__TThirdPartyComponentBase(Self).FSomeVar := 123;

Конечно, это опасно, потому что вам нужно будет контролировать изменения в базовом классе. Потому что, если расположение полей будет изменено, и вы пропустите этот факт, то приведенный выше подход приведет к сбоям, AV и т. Д.

0 голосов
/ 28 октября 2010

Не знаю, поможет ли это, но, похоже, я помню, что есть способ "взломать" приватную переменную в видимости.

Я знаю, например, что я столкнулся с предупреждениями от компилятора, когда я переместил свойство из более низкой видимости (в базовом классе) на более видимый уровень (в моем потомке). В предупреждении говорилось, что оно объявлено на другом уровне видимости ...

Прошло некоторое время, и я не уверен, но я верю, что вы можете сделать так, чтобы ваш потомок объявил ту же переменную, что и защищенный. (Возможно, вам придется использовать ключевое слово Redeclare для этого при компиляции.)

Извините, у меня нет более конкретной информации о том, как это сделать (если это действительно возможно.) Возможно, эта публикация побудит одного из волшебников здесь исправить меня! :-)

0 голосов
/ 28 октября 2010

Предоставьте доступ к значению приватной переменной защищенным свойством в 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;

В TMyFixedComponent классе используйте свойство MyVar в процедуре, которую вы хотите переопределить.

...