TBitBtn と TButton の継承チェーンはどうなりましたか?
-
09-09-2019 - |
質問
最近、RAD Studio 2007 プロジェクトを RAD Studio 2009 にアップグレードし始めました。私が気づいたことの 1 つは、一見単純なコードが突然コンパイルに失敗したことです。
コード例:
class CButtonPopupMenu
{
// Snip
public:
void Init( TButton* SrcButton )
{
SrcButton->OnClick = OnButtonClick;
}
private:
void __fastcall OnButtonClick( TObject* Sender )
{
// Do some button click stuff
}
};
// Snip
TButton button = new TButton( this );
TBitBtn bitBtn = new TBitBtn( this );
CButtonPopupMenu popupButton = new CButtonPopupMenu( button );
CButtonPopupMenu popupBitBtn = new CButtonPopupMenu( bitBtn );
これはすべてコンパイルに使用されますが、2009 では失敗します。2007 年の相続の流れをみる TBitBtn
から派生するために使用されます TButton
. 。したがって、ボタン コントロールで予期されるイベント (つまり、OnClick) が共有しました TButton
クラス。そのため、治療することができました TBitBtn
としてのクラス TButton
.
2007 年の継承チェーン:
- TBitBtn :Tボタン
2009 年の継承チェーン:
- TBitBtn :Tカスタムボタン
- Tボタン:Tカスタムボタン
2009年には両方とも Tボタン そして TBitボタン から派生する Tカスタムボタン, 、属性のようなボタンがそこに保持されていれば問題ないと思います。このような場合は、コードを変更して問題に対処するだけで済みます。 Tカスタムボタン その代わり。残念ながら、 Tカスタムボタン のようなものは保持しません クリック時. 。したがって、もう治療することはできません TBitBtn のような Tボタン. 。これらのクラスは両方とも、属性のような独自の個別のボタンを持ちます (つまり、どちらも独自の OnClick イベントを宣言しています)。つまり、少なくともインターフェイスか何かを提供する必要があります。 Iボタン その両方 Tボタン そして TBitBtn 埋め込む。
このような一見無害に見える変更が、不必要な大混乱を引き起こす可能性があるようです。これは奇妙に思えますが、CodeGear (またはさらに言うとフレームワーク作成者) がなぜこのようなことをするのか知っている人はいるでしょうか?
さらに重要なのは、この断片化された継承を考慮すると、 エレガント を治療するための解決策 TBitBtn のような Tボタン?
解決
のTButtonとTBitBtnはまだで開始するすべての方法ダウンのTControlレベルでそれが実装されているように、一般的なOnClickイベントを共有し続けるか、と常にされています。 TButtonのは、単にTBitBtnはその後、継承した、公表に保護されたのTControl :: OnClickイベントを推進しました。
D2009では、TCustomButtonは、他のTCustom ...クラスと同様、公表に基底クラスからprotectedメンバを促進しません。 TButtonとTBitBtnは個別に公表への保護のTControl :: OnClickイベントを推進しています。しかし、イベント自体がまだのTControlレベルで存在してます。
それはのTControlレベルで保護されているので、あなたはそれに到達するためにアクセサクラスを使用することができ、すなわちます:
class TCustomButtonAccess
{
public:
__property OnClick;
};
class CButtonPopupMenu
{
// Snip
public:
void Init( TCustomButton* SrcButton )
{
((TCustomButtonAccess*)SrcButton)->OnClick = OnButtonClick;
}
private:
void __fastcall OnButtonClick( TObject* Sender )
{
// Do some button click stuff
}
};
あるいは、任意の一般のTControlのポインタのための:
class TControlAccess
{
public:
__property OnClick;
};
class CControlPopupMenu
{
// Snip
public:
void Init( TControl* SrcControl )
{
((TControlAccess*)SrcControl)->OnClick = OnControlClick;
}
private:
void __fastcall OnControlClick( TObject* Sender )
{
// Do some click stuff
}
};
よりエレガントな解決策は、あなたがそのような自分のOnClickイベントを持っているTSpeedButtonなどのオブジェクトの他のタイプ、すなわちに対処できるようにすることになる、代わりにRTTIを使用することです。
#include <TypInfo.hpp>
class TControlAccess
{
public:
__property OnClick;
};
class CControlPopupMenu
{
// Snip
public:
void Init( TControl* SrcControl )
{
TMethod m;
m.Code = &OnControlClick;
m.Data = this;
SetMethodProp(SrcControl, "OnClick", m);
}
private:
void __fastcall OnControlClick( TObject* Sender )
{
// Do some click stuff
}
};
あるいは
#include <TypInfo.hpp>
class CObjectPopupMenu
{
// Snip
public:
void Init( TObject* SrcObject )
{
TMethod m;
m.Code = &OnObjectClick;
m.Data = this;
SetMethodProp(SrcObject, "OnClick", m);
}
private:
void __fastcall OnObjectClick( TObject* Sender )
{
// Do some click stuff
}
};
他のヒント
、私はをオペレーターとしてとでのとTCustomButtonクラスをお勧めします:
if (SrcButton is TButton) then
(SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
(SrcButton as TBitButton).OnClick := OnButtonClick;
C ++が長すぎる前に、単純です。
ところで、VCLはいつかボタンの間の単一のインタフェースを提供するために、アクション、メニューなどを含めると、コードを呼び出さなかった?