Как проверить, ссылаются ли две ссылки на метод на один и тот же метод?
Я пытаюсь сделать список обработчиков событий, где обработчик-это эталонный метод. Чтобы удалить конкретный обработчик, мне нужно найти его в списке. Но как я могу сравнить адрес кода двух ссылок на метод?
type
TEventHandler = reference to procedure;
procedure TestProc;
begin
end;
procedure TForm26.FormCreate(Sender: TObject);
var
Handlers: TList<TEventHandler>;
begin
Handlers := TList<TEventHandler>.create;
try
Handlers.Add(TestProc);
Handlers.Remove(TestProc); { doesn't work }
Assert(Handlers.Count=0); { fails }
Assert(Handlers.IndexOf(TestProc)>=0); { fails }
finally
FreeAndNil(Handlers);
end;
end;
default comparer of TList не сравнивает ссылки на методы должным образом. Как я могу их сравнить? Есть ли структура, подобная TMethod, но для ссылок на методы?
1 ответов
Это не так просто, как может показаться.
чтобы понять, почему это происходит, вам нужно понять, как назначение ссылки на метод выполняется компилятором.
код, который вы написали, в основном переведен в это компилятором:
Handlers.Add(procedure begin TestProc; end);
Handlers.Remove(procedure begin TestProc; end);
теперь мы должны знать, что если у вас есть несколько анонимных методов в одной и той же процедуре, они на самом деле разные анонимные методы, даже если их код идентичен. (см. как анонимный методы, реализованные под капотом?)
Это означает, что значения, которые передают Add
и Remove
отличаются, даже если код в их телах одинаковый - даже при взломе вокруг него потребуется анализ двоичного кода, чтобы определить, является ли код внутри тела одинаковым.
если вы измените код следующим образом, он будет работать, потому что тогда у вас есть только один анонимный метод - для этого он работает, но обычно вы не добавляете и удаляете внутри точно такая же процедура:
var
Handlers: TList<TEventHandler>;
Handler: TEventHandler;
begin
Handlers := TList<TEventHandler>.create;
try
Handler := TestProc;
Handlers.Add(Handler);
Handlers.Remove(Handler);
Assert(Handlers.Count=0);
finally
FreeAndNil(Handlers);
end;
end;
Если вам нужен список, в котором вы добавляете и удаляете обработчики событий, моя личная рекомендация-избегать анонимного типа метода и использовать процедуру или методы:
type
TEventHandlerA = procedure;
TEventHandlerB = procedure of object;
решение, какой из них лучше, зависит от вас, потому что вы лучше знаете свой код.