4

我最近开始将我的 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 : T按钮

2009继承链:

  • TBitBtn : TCustomButton
  • TButton : TCustomButton

在 2009 年,TButtonTBitButton都派生自TCustomButton,我想如果按钮之类的属性被保存在那里就可以了。如果是这种情况,我可以更改代码来处理TCustomButton。不幸的是,TCustomButton不包含OnClick之类的东西。因此,我不能再将TBitBtn视为TButton。这两个类现在都有自己独立的按钮类属性(即它们都声明了自己的 OnClick 事件)。我的意思是,至少提供一个接口或其他东西,比如TButton和TButton的IButtonTBitBtn实现。

似乎这些看似无辜的变化会造成不必要的破坏。这似乎很奇怪,我想知道是否有人知道为什么 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,它还允许您处理其他类型的对象,例如 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
    }
};
于 2009-06-03T19:45:24.147 回答
1

如果这是 Delphi,我建议使用带有isas运算符的 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 回答