2009-06-03 4 views
3

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

Пример кода:

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?

ответ

6

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

Отличная информация! В чем разница между двумя последними примерами RTTI? –

+0

Первый работает с указателями TObject и, таким образом, применяется ко всем типам объектов VCL независимо от их иерархии, тогда как второй работает только с TControl и потомками. –

1

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

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

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

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

+0

Был бы заинтересован в этих действиях, которые вы указали. Должен будет выглядеть так. –

+0

В C++ я думаю, что можно было бы проверить NULL! = Dynamic_cast (SrcButton), чтобы определить, был ли SrcButton фактически TButton. –

+0

Учебное пособие по действиям http://www.blong.com/Conferences/BorCon2003/Actions/6102.htm – devio