2010-10-28 45 views
10

我需要修復第三方組件。這個組件的類有積極使用其子孫私有變量: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再創建一個新的類,但我需要用我的修補程序。而且我真的不想在我的代碼中再現基類的所有樹。

你可以建議一些快速入侵訪問該私人領域而不改變原始組件的單位如果可能的話?

回答

5

您必須使用hack才能訪問其他單元中的任何類(包括基類)中的私有字段。在你的情況定義您的單位:

type 
    __TThirdPartyComponentBase = class 
    private 
    FSomeVar: Integer; 
    end; 

然後得到訪問:

__TThirdPartyComponentBase(Self).FSomeVar := 123; 

當然,這是危險的,因爲你需要控制在基類的變化。因爲如果字段佈局將會改變並且你會錯過這個事實,那麼上述方法將導致失敗,AV等等。

+2

@Andrew:請注意,只要祖先(第三方)組件的內存佈局發生變化,此解決方案就會中斷。你可能沒有注意到它會中斷,因爲沒有任何東西會提醒你。或者你可能會看到虛假的錯誤行爲(如果你幸運的話:訪問違規),因爲你開始覆蓋不屬於你的數據。 – 2010-10-28 15:50:07

+0

@Jeroen Pluimers我已經注意到Andrew關於這個事實。但是這個問題沒有其他解決方案。 – oodesigner 2010-10-28 16:14:04

+0

類助手可以做到這一點,沒有黑客攻擊,看到我的答案:) – 2012-07-07 22:19:47

-1

通過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屬性中,你想覆蓋的過程。

+5

但是這會改變TThirdPartyComponent的原始代碼。我想要一些解決方案而不重寫TThirdPartyComponent的代碼或更改原始組件的單元。 – Andrew 2010-10-28 14:08:20

0

不知道這是否有用,但我似乎記得有一種方法將私有變量「破解」爲可見性。

例如,我知道,當我將屬性從較低可見性(在基類中)移動到更可見的級別(在我的後代)時,我遇到了編譯器的警告。警告聲明它在不同的可見度水平被宣佈...

已經有一段時間了,我不確定,但我相信你可以做的是在你的後代中聲明與保護相同的變量。 (您可能需要使用Redeclare關鍵字進行編譯。)

對不起,我沒有關於如何做到這一點的更多具體信息(如果確實有可能)。也許這篇文章會提示這裏的一個嚮導改正我! :-)

18

通過使用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屬性的訪問是通過類型強制轉換爲基類來實現的。

+1

非常好!我喜歡。 – 2014-09-18 14:13:07

+3

請注意,從Delphi 10.1柏林開始,其他單位的班級助手不能再訪問私人成員,因爲這被認爲是Embarcadero的一個bug。有關更多詳細信息,請參閱http://stackoverflow.com/questions/9410485/how-do-i-use-class-helpers-to-access-strict-private-members-of-a-class#comment61026976_9410717。 – 2016-04-21 05:14:43