重新调整实例类内存分配

时间:2013-12-07 12:16:29

标签: delphi class instance

我在运行时将此示例用于更改类:

procedure PatchInstanceClass(Instance: TObject; NewClass: TClass);
type
  PClass = ^TClass;
begin
  if Assigned(Instance) and Assigned(NewClass)
    and NewClass.InheritsFrom(Instance.ClassType)
    and (NewClass.InstanceSize = Instance.InstanceSize) then
  begin
    PClass(Instance)^ := NewClass;
  end;
end;

type
  TMyButton = class(TButton)
  Private
    FLogEvent : TNotifyEvent;
  public
    Property  Log : TNotifyEvent Read FLogEvent Write FLogEvent;
    procedure Click; override;
  end;

procedure TMyButton.Click;
begin
  Inherited;
  if Assigned(FLogEvent) then
     FLogEvent(Self);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  PatchInstanceClass(Button1, TMyButton);
end;

我的问题是TMyButton Size与TButton不同因为我将这个NotifyEvent添加到TMyButton。 我的问题是如何重新调整NewClass相同实例的内存分配。 : - )

2 个答案:

答案 0 :(得分:1)

您尝试做的事情是不切实际的。如果可以找到对象的所有引用,也可以这样做。您需要这样做,因为重新分配可能导致内存驻留在不同的地址。因此您需要更新这些参考文献。

所以你需要找到:

  • 代码中的所有引用,以及
  • VCL代码中的所有引用。

后者要困难得多。父母持有对其子女的引用。按钮的所有者保存引用。如果我没记错的话,行动框架可以包含参考文献。我确信还有其他地方可以参考。

如果您能找到所有参考文献,那么您需要:

  1. 使用新大小重新分配内存块。
  2. 对派生类中的新字段执行任何初始化。
  3. 更改对象的类。
  4. 更新对象的所有引用。
  5. 坦率地说,这是测试UI事件的一种可怕方式。你有很多更好的选择:

    • 使用插入类。
    • 使用虚方法拦截器。
    • 处理应用程序的OnMessage事件以过滤消息队列。

    你真的应该放弃目前解决问题的方法。

答案 1 :(得分:1)

虽然我同意其他人的意见 - 你应该重新考虑一下你的方法,因为这闻起来有点俗气。但是我喜欢讨厌的东西 - 所以我要告诉你如何实现你所要求的。

关键是不要在新类中放置任何东西(因为InstanceSize需要相同)把它放在其他地方 - 如果你使用的是更新的Delphi版本(2010或更高版本),你可以这样做。否则你需要修改一下代码,但我想你明白了:

uses
  Generics.Collections;

procedure PatchInstanceClass(Instance: TObject; NewClass: TClass);
begin
  if Assigned(Instance) and Assigned(NewClass)
    and NewClass.InheritsFrom(Instance.ClassType)
    and (NewClass.InstanceSize = Instance.InstanceSize) then
  begin
    PPointer(Instance)^ := NewClass;
  end;
end;

type
  TMyButton = class(TButton)
  private
    function GetLogEvent: TNotifyEvent;
    procedure SetLogEvent(const Value: TNotifyEvent);
  public
    destructor Destroy; override;
    procedure Click; override;

    property LogEvent: TNotifyEvent read GetLogEvent write SetLogEvent;
  end;

  TMyButtonHelper = class helper for TMyButton
  private
    class var fLogEvents: TDictionary<TObject, TNotifyEvent>;
  public
    class constructor Create;
    class destructor Destroy;
  end;

{ TMyButtonHelper }

class constructor TMyButtonHelper.Create;
begin
  fLogEvents := TDictionary<TObject, TNotifyEvent>.Create;
end;

class destructor TMyButtonHelper.Destroy;
begin
  fLogEvents.Free;
end;

{ TMyButton }

destructor TMyButton.Destroy;
begin
  fLogEvents.Remove(Self);
  inherited;
end;

procedure TMyButton.Click;
begin
  inherited;
  if Assigned(LogEvent) then
     LogEvent(Self);
end;

function TMyButton.GetLogEvent: TNotifyEvent;
begin
  fLogEvents.TryGetValue(Self, Result);
end;

procedure TMyButton.SetLogEvent(const Value: TNotifyEvent);
begin
  fLogEvents.AddOrSetValue(Self, Value);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  PatchInstanceClass(Button1, TMyButton);
  TMyButton(Button1).LogEvent := Button1Click;
end;