In my previous post I discussed the problem that method pointers aren’t able to store references to anonymous methods. Unfortunately that limitation makes anonymous methods less useful since they can’t be used to set up event handlers on the fly, like so:
// Wouldn't it be great // if we could do something // like this? Memo1.OnKeyPress := procedure(Sender: TObject; var Key: Char) begin Key := Chr(Ord(Key) + 1); end;
One thing I wasn’t aware of when I wrote that previous post is that while method pointers can’t be used to store anonymous methods, the opposite is true: Method references can indeed store both anonymous methods and instance methods. Many thanks to Barry Kelly who pointed it out to me in a comment.
Could this be the way to go? Shall we all start using method references instead of method pointers when we declare the events of our components from now on? Let’s take a look shall we? But just so that we’re clear with what we mean – there are many ways to say the same thing – here are the definitions I’m using in this post.
First of all, when I say method I mean either a function or a procedure, and it can be any of the types below. This is different from the common definition stating that methods are subroutines associated with either a class or an object.
These are functions and procedures declared outside the context of a class.
procedure PlainProcedure; begin // Has no Self pointer end;
Functions or procedures associated to an object instance
Procedure TSomeClass.InstanceProcedure; begin // Self is an instance of TSomeClass end;
Functions or procedures associated to a class. In other languages also called static methods.
class procedure TSomeClass.ClassProcedure; begin // Self is TSomeClass end;
Functions or procedures declared as they are assigned in an execution context. These methods capture the surrounding context and may use local variables and arguments even if they’re out of execution scope.
SomeItems.Sort( function(Item1, Item2: TSomeItem): Integer; begin if Item1.Value &gt; Item2.Value then Result := 1 else if Item1.Value &lt; Item2.Value then Result := -1 else Result := 0; end );
For these four types of methods there are three reference types to store pointers for later invocation of the methods.
Plain method pointer
type TPlainMethodPointer = procedure(ASender: TObject);
type TMethodPointer = procedure(ASender: TObject) of object;
type TMethodReference = reference to procedure(ASender: TObject);
This compatibility graph shows what method types can be stored with the respective reference type.
|Plain method pointer||YES||no||no||no|
From this graph we can see that the Method reference type is the unifying type that can store all types of method references. It seems like the way to go is to embrace Method references and render the other reference types obsolete. Is it possible? The answer is yes but no.
Take the event example of my last post. The VCL can not be changed to utilize Method pointers for backward compatibility reasons (like the widespread use of TMethod casts) so there’s nothing to do about it there. But what about our own components, and components created from now on?
type TNotifyMethod = reference to procedure(Sender: TObject); TMyComponent = class(TComponent) ... published property OnChange: TNotifyMethod read FOnChange write FOnChange; end;
Well, it works fine as long as you set up the event handlers (OnChange in the above example) dynamically, but the method reference type events do not play well with the Delphi 2009 IDE. The object inspector doesn’t recognize possible event handlers and won’t create one automatically I you double click the event property. If you try to force the assignment by giving the name of an existing event handler method explicitly, the IDE throws an ugly Invalid Property Error Dialog.
As much as I’d like to be able to assign anonymous methods as event handlers to my components’, I’m not prepared to sacrifice the IDE integration. Hopefully CodeGear will fix this issue in future releases but until then anonymous methods will remain less useful than they could be.