I am trying to make list of event handlers where handler is method reference. To delete specific handler i need to find it in the list. But how can i compare code address of two method references?
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<> doesn't compare method references properly. How can i compare them? Is there structure similar to TMethod but for method references?
There are four kinds of method references: Static methods. Instance methods of particular objects. Instance methods of an arbitrary object of a particular type.
Java provides a new feature called method reference in Java 8. Method reference is used to refer method of functional interface. It is compact and easy form of lambda expression. Each time when you are using lambda expression to just referring a method, you can replace your lambda expression with method reference.
A static method reference refers to a static method in a specific class. Its syntax is className::staticMethodName , where className identifies the class and staticMethodName identifies the static method. An example is Integer::bitCount .
This is not as easy as it might seem.
To understand why this happens you need to understand how assigning to a method reference is performed by the compiler.
The code you wrote is basically translated into this by the compiler:
Handlers.Add(procedure begin TestProc; end);
Handlers.Remove(procedure begin TestProc; end);
Now we have to know that if you have multiple anonymous methods within the same routine they are in fact different anonymous methods even if their code is identical. (see How are anonymous methods implemented under the hood?)
This means that the values passed to Add
and Remove
are different even if the code in their bodies is the same - even with hacking around it would require a binary code analysis to determine if the code inside the body is the same.
If you would change the code as follows it would work because then you only have one anonymous method - for this snipped it works but usually you would not add and remove within the exact same routine:
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;
If you want a list where you add and remove event handlers my personal recommendation is to avoid an anonymous method type and use procedure or methods:
type
TEventHandlerA = procedure;
TEventHandlerB = procedure of object;
The decision which one is better is up to you because you know your code better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With