2013-12-07 3 views
3

I Используйте этот образец для Change класса во время выполнения:Изменение размера экземпляра класса Распределение памяти

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 Размер отличается от TButton Поскольку я Добавить это NotifyEvent в TMyButton. Мой вопрос: как я могу изменить размер памяти для NewClass того же экземпляра. :-)

+1

Используйте класс промежуточных элементов, и вам не понадобится взлом. – TLama

+2

Что вы пытаетесь достичь? Это неверное решение с вероятностью> 0,999. –

+0

Вы не должны этого делать, потому что изменение размера экземпляра объекта обычно перемещает экземпляр в память и делает ссылки на экземпляр недействительными. – kludg

ответ

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; 
+0

Лучше просто использовать перехватчик виртуальных методов, когда вы это делаете –

+0

@Farzad Не задал вопрос, как изменить размер экземпляра? –

1

То, что вы пытаетесь сделать, нецелесообразно. Возможно, вы можете сделать это, если сможете найти все ссылки на объект. Вам нужно будет сделать это, потому что перераспределение может привести к тому, что память будет находиться на другом адресе. И поэтому вам нужно будет обновить эти ссылки.

Таким образом, вы должны были бы найти:

  • Все ссылки в коде, и
  • Все ссылки в коде VCL.

Последнее намного сложнее. Родитель держит ссылки на своих детей. Владелец кнопки содержит ссылки. Рамка действий может содержать ссылки, если я правильно помню. Я уверен, что есть другие места, где делаются ссылки.

Если бы вы могли найти все ссылки, то вам необходимо:

  1. Перераспределение блока памяти с новым размером.
  2. Выполните любую инициализацию новых полей в производном классе.
  3. Измените класс объекта.
  4. Обновить все ссылки на объект.

Честно говоря, это ужасный способ проведения пользовательских пользовательских событий. У вас есть много вариантов:

  • Используйте класс промежуточного элемента.
  • Использование перехватчиков виртуальных методов.
  • Обработать событие OnMessage приложения для фильтрации очереди сообщений.

Вы действительно должны отказаться от своего текущего решения проблемы.

Смежные вопросы