当使用下面的定义实现事件时,Spring4D将添加和调用方法,但不会删除处理程序(使用IEvent
{$M+}
TaskItemChangeEvent = reference to procedure(const TaskItem: ITaskItem; Event: TTaskListEvent);
下面的代码可以工作,但是我不希望被强制绑定到一个对象。
{$M+}
TaskItemChangeEvent = procedure(const TaskItem: ITaskItem; Event: TTaskListEvent) of Object;
我认为问题在于TEventBase中的这一行。Remove作为对procedure的引用不是TMethod?
if TMethod(handlers[i]) = TMethod(handler) then
原因是编译器可能会在添加和删除匿名方法的地方创建不同的实例。
请看下面的代码:
var
proc: TProc;
procedure Add(p: TProc);
begin
proc := p;
end;
procedure Remove(p: TProc);
begin
Writeln(PPointer(@proc)^ = PPointer(@p)^);
end;
procedure A;
var
p: TProc;
begin
p := procedure begin end;
Add(p);
Remove(p);
end;
procedure B;
begin
Add(procedure begin end);
Remove(procedure begin end);
end;
procedure C;
begin
Add(A);
Remove(A);
end;
begin
A;
B;
C;
Readln;
end.
您将注意到,在B
和C
中,它将打印False
,因为传递给Add
和Remove
的两个匿名方法彼此不同。虽然在B
中很明显,但在C
中它不是,但编译器实际上将代码转换为:
procedure C;
begin
Add(procedure begin A(); end);
Remove(procedure begin A(); end);
end;
这意味着如果你想使用IEvent<>
和一个方法引用类型,并且能够删除,你需要保持你添加的引用,以便它们相等,从而能够在调用Remove
时被找到。
在TEventBase
内部的引用都被作为TMethod
处理的事实与此无关-当传递一个匿名方法时,它被转换为TMethod
。毕竟,匿名方法类型是由编译器创建的对象支持的接口,这使得进行这种转换成为可能,并导致必须保留为了删除它而添加的引用。