4

私は最近、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年では失敗しています。TBitBtnから派生するために使用される2007 年の継承チェーンを調べTButtonます。したがって、任意のボタン コントロール (つまり、OnClick) で予期されるイベントは、TButtonクラスによって共有されました。したがって、私は自分のTBitBtnクラスを として扱うことができましたTButton

2007 継承チェーン:

  • TBitBtn : TButton

2009 継承チェーン:

  • TBitBtn : TCustomButton
  • TButton : TCustomButton

2009 年には、TButtonTBitButtonの両方がTCustomButtonから派生しています。これは、ボタンのような属性がそこに保持されていれば問題ないと思います。この場合は、代わりにTCustomButtonを処理するようにコードを変更するだけで済みます。残念ながら、TCustomButtonはOnClickのようなものを保持しません。したがって、 TBitBtnをTButtonのように扱うことはできなくなりました。これらのクラスは両方とも、独自の個別のボタンのような属性を持っています (つまり、両方とも独自の OnClick イベントが宣言されています)。つまり、少なくとも、TButtonTBitBtn実装。

これらの一見無害な変更は、不必要な大混乱を引き起こす可能性があるようです。これは奇妙に思えますが、なぜ CodeGear (またはフレームワークの作成者) がこの種のことを行うのかを誰かが知っているかどうか疑問に思っていますか?

さらに重要なことは、この断片化された継承を考えると、 TBitBtnをTButtonのように扱う洗練された解決策はありますか?

4

2 に答える 2

7

TButton と TBitBtn は、共通の OnClick イベントを引き続き共有します。これは、最初から TControl レベルで実装されており、常にそうであったためです。TButton は、保護された TControl::OnClick イベントを発行済みに昇格させただけであり、TBitBtn はそれを継承します。

D2009 では、TCustomButton は、他の TCustom... クラスと同様に、保護されたメンバーを基本クラスから公開に昇格させません。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
    }
};

より洗練された解決策は、代わりに RTTI を使用することです。これにより、独自の OnClick イベントを持つ TSpeedButton などの他のタイプのオブジェクトも処理できるようになります。

#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
    }
};
于 2009-06-03T19:45:24.147 に答える
1

これが Delphi の場合、isおよびas演算子を使用した TCustomButton クラスをお勧めします。

if (SrcButton is TButton) then
  (SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
  (SrcButton as TBitButton).OnClick := OnButtonClick;

C++ はあまりにも昔のことです

ところで、VCL には、ボタン、メニューなどと呼び出されたコードの間の単一のインターフェイスを提供するためのアクションが含まれていませんでしたか?

于 2009-06-03T20:03:02.863 に答える