Что случилось с цепочкой наследования TBitBtn и TButton?

StackOverflow https://stackoverflow.com/questions/946316

Вопрос

Недавно я начал обновлять свой проект 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 года:

  • ТБитБтн:TКнопка

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

  • ТБитБтн:ТCustomButton
  • Кнопка:ТCustomButton

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

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

Что еще более важно, учитывая это фрагментированное наследование, существует ли и элегантный решение для лечения ТБитБтн как TКнопка?

Это было полезно?

Решение

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
    }
};

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top