Delphi: запись в поле частного предка в классе потомков
Мне нужно исправить сторонний компонент. Этот класс компонента имеет частную переменную, которая активно используется ее потомками:
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, но мне нужно использовать его для моего исправления. И я действительно не хочу воспроизводить в своем коде все это дерево базовых классов.
Можете ли вы посоветовать быстрый хак для доступа к этому частному полю без изменения исходного компонента, если это вообще возможно?
Ответы
Ответ 1
Вы должны использовать хак для доступа к частному полю в любом классе (включая базовый класс) в другом блоке. В вашем случае укажите в своем устройстве:
type
__TThirdPartyComponentBase = class
private
FSomeVar: Integer;
end;
Затем получите доступ:
__TThirdPartyComponentBase(Self).FSomeVar := 123;
Конечно, это опасно, потому что вам нужно будет контролировать изменения в базовом классе. Потому что, если макет полей будет изменен, и вы пропустите этот факт, тогда вышеупомянутый подход приведет к сбоям, AV и т.д.
Ответ 2
Используя 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
вспомогательного класса базового класса выполняется с помощью типа cast в базовый класс.
Ответ 3
Не знаю, поможет ли это, но я, похоже, помню, что есть способ "взломать" личную переменную в видимость.
Я знаю, например, что я встретил предупреждения от компилятора, когда я переместил свойство из более низкой видимости (в базовом классе) на более видимый уровень (у моего потомка). Предупреждение заявило, что оно объявляется на другом уровне видимости...
Это было какое-то время, и я не уверен, но я верю, что вы можете сделать это в своем потомке, объявив ту же переменную, что и защита. (Возможно, вам придется использовать ключевое слово Redeclare для компиляции.)
Извините, у меня нет более конкретной информации о том, как это сделать (если это действительно возможно.) Возможно, это сообщение подскажет одному из мастеров здесь исправить меня!:-)
Ответ 4
Вывести значение частной переменной защищенным свойством в 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
в процедуре, которую вы хотите переопределить.