1

Sometimes I have a random thought and this is my latest. Quite often control events don't reference the form/datamodule that they are attached to, which somewhat meets the definition of a class method. Apart from the IDE complaining (which is pretty much a showstopper as it is so annoying), is there any technical reason why one would not do this.

For instance:

class procedure TForm9.Button1Click(Sender: TObject);
begin
  ShowMessage('Hello World');
end;

I've briefly tried this out and seems to work.

14
  • 1
    What would be the point? You couldn't use any variables/controls, so why have it part of a visual container in the first place? You can't access the Sender, so you could only attach it to a single button (which would have to exist in the first place to be clicked, so there's no reason for it to be a class procedure). Commented Feb 11, 2014 at 23:47
  • 1
    @KenWhite The sender would still be accessible. I like making methods class methods in that it makes it clear that that method will not change the state of the class (which is nice to know when debugging) and it is a good indication that the method may belong elsewhere, and you can usually shuffle them between classes without too much thought. Commented Feb 11, 2014 at 23:55
  • 1
    @KenWhite Maybe I am being silly and I am missing something obvious but why can't you access sender? It's passed in as a parameter. Commented Feb 11, 2014 at 23:55
  • 2
    Of course you can access the Sender, it's a normal parameter. You cannot access any members of TForm9 but like in this example this is not always necessary. The reason is rather because methods consist of the Data and the code pointer and the Data is always the instance pointer and not as in the case of a class procedure the class pointer. Commented Feb 11, 2014 at 23:56
  • 2
    See also this question for a use case. Commented Feb 12, 2014 at 6:01

2 Answers 2

8

A non-static class method has a Self pointer. That Self is the class. Non-static class methods have a Self in order to support the dispatch of virtual class methods.

Because non-static class methods have Self it means that they can be assigned to of object method types as needed for event handlers. So you can certainly write in Pascal code a runtime assignment:

Button1.OnClick := TForm1.SomeClassMethod;

However, the IDE does not support class methods as event handlers. I believe there is a good reason for this. When the streaming code encounters the setting it looks like this in the .dfm file:

OnClick = SomeClassMethod

This can only appear in the .dfm file when you put it in manually. That's what I think you mean when you say the that the IDE complains. You added the above to the .dfm file and whenever you load the form the IDE rejects it as invalid.

Now, supposing you do this anyway. Then the streaming code encounters the above .dfm setting as text. It needs to use RTTI to find SomeClassMethod. Which is fine. It then synthesises a method value from a data pointer and a code pointer. It uses the instance as the data pointer. And that means that the method is invalid. You cannot pair instance and class method. But the streaming code knows no better and is built on the assumption that the methods found in .dfm files are instance methods. A reasonable assumption given that the designer only offers instance methods.

The code that implements this can be found in TReader.FindMethodInstance:

function TReader.FindMethodInstance(Root: TComponent; const MethodName: string): TMethod;
var
  Error: Boolean;
begin
  if Assigned(FOnFindMethodInstance) then
  begin
    Result.Code := Root.MethodAddress(MethodName);
    Result.Data := Root;
    Error := Result.Code = nil;
    FOnFindMethodInstance(Self, MethodName, Result, Error);
  end else
    Error := True;
  if Error then
  begin
    Result.Data := Root;
    Result.Code := FindMethod(Root, MethodName);
  end;
end;

The pertinent code is assignment of Root to Result.Data because Root is the instance.

So, when the event fires, your method runs but any attempt to use Self fails. Because the streaming code has given you an invalid Self, an instance instead of a class.

The streaming code cannot properly hook up a class method. And the designer therefore won't offer class methods to use as event handlers. But from code you can perfectly well assign class methods to event handlers and everything works.

Sign up to request clarification or add additional context in comments.

Comments

6

You cannot bind them using the form designer or object inspector but you can directly assign them:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnClick := TForm1.Button1Click;
end;

class procedure TForm9.Button1Click(Sender: TObject);
begin
  ShowMessageFmt('%s says hello %s', [(Sender as TComponent).Name, Self.ClassName]);
end;

will show: Button1 says hello TForm9

It might not make lot of sense if the control and the event handler are in the same class as this class needs to have an instance in order for the control to trigger the event but in many cases they are different classes/instances that are wired together using events.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.