Что случилось с цепочкой наследования TBitBtn и TButton? - PullRequest
4 голосов
/ 03 июня 2009

Я недавно начал обновлять свой проект RAD Studio 2007 до RAD Studio 2009. Одна вещь, которую я заметил, это то, что казалось бы простой код неожиданно не скомпилировался.

Пример кода:

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: TButton

2009 цепочка наследования:

  • TBitBtn: TCustomButton
  • TButton: TCustomButton

В 2009 году и TButton и TBitButton являются производными от TCustomButton , что было бы неплохо, если бы там были атрибуты, подобные кнопкам. Если бы это было так, я мог бы просто изменить код, чтобы иметь дело с TCustomButton . К сожалению, TCustomButton не содержит таких вещей, как OnClick . Поэтому я больше не могу лечить TBitBtn как TButton . Оба этих класса теперь имеют свои отдельные атрибуты, подобные кнопкам (то есть у них обоих объявлено собственное событие OnClick). Я имею в виду, по крайней мере, предоставить интерфейс или что-то подобное, например, IButton , которые оба TButton и TBitBtn реализуют.

Кажется, что эти типы, казалось бы, невинных изменений могут вызвать ненужный хаос. Это кажется странным, и мне интересно, знает ли кто-нибудь, почему CodeGear (или любой другой разработчик Framework) будет делать такие вещи?

Что еще более важно, учитывая это фрагментированное наследование, существует ли и элегантное решение для обработки TBitBtn как TButton ?

Ответы [ 2 ]

7 голосов
/ 03 июня 2009

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, что также позволило бы вам обрабатывать другие типы объектов, такие как TSpeedButton, которые имеют свое собственное событие OnClick, например:

#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
    }
};
1 голос
/ 04 июня 2009

Если бы это был Delphi, я бы предложил класс TCustomButton с is и в качестве операторов:

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

C ++ просто слишком давно

Кстати, разве VCL когда-нибудь не включал Действия, чтобы обеспечить единый интерфейс между кнопками, меню и т. Д. И вызванным кодом?

...