The need to Invoke() an action has nothing to do with the calling code being inside or outside its class, it's necessary to make sure that methods operating on controls are called only in the context of the thread the control was created in. Controls have a thread affinity, which is nothing specific to C# or .NET, it is inherent in the way Windows works. Messages for a control / window can be handled in the thread it was created in only. This holds true for Delphi as well, and the whole VCL is not thread-safe either.
The closest thing to Invoke() the Delphi VCL has is the Synchronize() method. It is used within secondary threads to schedule code to be executed in the context of the main VCL thread, the user interface thread in which all VCL controls need to be created, in which consequently all Windows messages for those controls are handled, and which is the only one really safe for calling any of the control methods.
A Delphi program does not create any secondary threads on its own, you will have to do this yourself. But unless you do, everything happens in the main thread context and there is no need to call Synchronize(). You can simply call the method you need to call.
To stay with your example: You set new text for an edit control like so:
Form1.Edit1.Text := 'foo bar baz';
This assumes that the unit containing the TForm1
class contains a variable Form1
, which it usually does. This is similar to the singleton in your C# code. It works because all controls dropped onto the form will have public visibility.
Off-topic-remark:
Note that while this is fairly common code it violates the Law of Demeter. Changing the controls in the form will make it necessary to subsequently change all code that accesses them in this way. But this is another discussion entirely.