Как проверить, указывают ли два события на одну и ту же процедуру в Delphi?
Скажем, у меня есть событие Button1.OnClick, связанное с процедурой Button1Click. У меня также есть Button2.OnClick, связанный с какой-либо другой процедурой. Как проверить, что оба события связаны с другой или той же процедурой из среды выполнения?
Я попытался проверить, если:
- Button1.OnClick = Button2.OnClick, но это дало мне ошибку (недостаточно фактических параметров)
- @(Button1.OnClick) = @(Button2.OnClick), ошибка снова (недостаточно фактических параметров)
Как проверить его правильно?
Ответы
Ответ 1
Ссылка на метод может быть разбита на две части, указатель на объект и указатель на сам метод. Существует удобный тип записи, определенный в модуле System
под названием TMethod
, который позволяет нам это разрушить.
С помощью этого знания мы можем написать примерно следующее:
function SameMethod(AMethod1, AMethod2: TNotifyEvent): boolean;
begin
result := (TMethod(AMethod1).Code = TMethod(AMethod2).Code)
and (TMethod(AMethod1).Data = TMethod(AMethod2).Data);
end;
Надеюсь, это поможет.:)
Изменить: просто чтобы выложить в лучшем формате проблему, которую я пытаюсь решить здесь (как указано в комментариях).
Если у вас две формы, оба экземпляра из одного базового класса:
Form1 := TMyForm.Create(nil);
Form2 := TMyForm.Create(nil);
и вы назначаете тот же метод из этих форм на две кнопки:
Button1.OnClick := Form1.ButtonClick;
Button2.OnClick := Form2.ButtonClick;
И сравните два свойства OnClick
, вы обнаружите, что Code
одинаков, но Data
отличается. Это потому, что он тот же метод, но на двух разных экземплярах класса...
Теперь, если у вас было два метода на одном и том же объекте:
Form1 := TMyForm.Create(nil);
Button1.OnClick := Form1.ButtonClick1;
Button2.OnClick := Form1.ButtonClick2;
Тогда их Data
будут одинаковыми, но их Code
будут разными.
Ответ 2
Я делаю это с помощью этой функции:
function MethodPointersEqual(const MethodPointer1, MethodPointer2): Boolean;
var
Method1: System.TMethod absolute MethodPointer1;
Method2: System.TMethod absolute MethodPointer2;
begin
Result := (Method1.Code=Method2.Code) and (Method1.Data=Method2.Data)
end;
Это работает, но если кто-то знает менее хакерский способ сделать это, я хотел бы услышать об этом!
Ответ 3
Я знаю, что это старый вопрос... но вот мои 2центы...
Это ответ похож на Nat, но не ограничивает нас только TNotifyEvents... и отвечает на вопрос Дэвида о том, как это сделать, если это будет взлом...
function CompareMethods(aMethod1, aMethod2: TMethod): boolean;
begin
Result := (aMethod1.Code = aMethod2.Code) and
(aMethod1.Data = aMethod2.Data);
end;
Я использую его так
procedure TDefaultLoop.RemoveObserver(aObserver: TObject; aEvent: TNotifyEvent);
var
a_Index: integer;
begin
for a_Index := 0 to FNotifyList.Count - 1 do
if Assigned(FNotifyList[a_Index]) and
(TNotify(FNotifyList[a_Index]).Observer = aObserver) and
CompareMethods(TMethod(TNotify(FNotifyList[a_Index]).Event), TMethod(aEvent)) then
begin
FNotifyList.Delete(a_Index);
FNotifyList[a_Index] := nil;
end;
Также быстрая и грязная демонстрация
procedure TForm53.Button1Click(Sender: TObject);
var
a_Event1, a_Event2: TMethod;
begin
if Sender is TButton then
begin
a_Event1 := TMethod(Button1.OnClick);
a_Event2 := TMethod(Button2.OnClick);
if CompareMethods(TMethod(TButton(Sender).OnClick), a_Event1) then
ShowMessage('Button1Click Same Method');
if CompareMethods(TMethod(TButton(Sender).OnClick), a_Event2) then
ShowMessage('Button2Click Same Method');
end;
end;
procedure TForm53.Button2Click(Sender: TObject);
var
a_Event1, a_Event2: TMethod;
begin
if Sender is TButton then
begin
a_Event1 := TMethod(Button1.OnClick);
a_Event2 := TMethod(Button2.OnClick);
if CompareMethods(TMethod(TButton(Sender).OnClick), a_Event1) then
ShowMessage('Button1Click Same Method');
if CompareMethods(TMethod(TButton(Sender).OnClick), a_Event2) then
ShowMessage('Button2Click Same Method');
end;
end;