Что случилось с цепочкой наследования TBitBtn и TButton?
Я недавно начал обновлять свой проект 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 ответа
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 с операторами is и as:
if (SrcButton is TButton) then
(SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
(SrcButton as TBitButton).OnClick := OnButtonClick;
C++ просто слишком давно
Кстати, VCL когда-нибудь включал в себя действия, чтобы обеспечить единый интерфейс между кнопками, меню и т. д. и вызванным кодом?