Oxygene supports implementation interfaces inline in a method as part of an expression. This is especially helpful when working with delegate based APIs (not to be confused with Blocks, also referred to as delegates on .NET), where a class implementing one or more methods of an interface is used for callbacks and events.
Oxygene extends the new class syntax (also used for Anonymous Classes), by allowing you to provide the name of either an interface to implement or an (abstract or concrete) base class to descend from.
So how might this work? Let's have a look at the following (fictitious) type:
type IButtonDelegate = interface method OnClick(aButton: Button); method OnGetHoverHint(aButton: Button): String; end; // optionally, this class might exist as well BaseButtonDelegate = class(IButtonDelegate) method OnClick(aButton: Button); virtual; empty; method OnGetHoverHint(aButton: Button): String; virtual; empty; end; Button = class private public property Caption: String; property Events: IButtonDelegate; end;
We have a button class, along with a delegate interface that defines its events. Whenever the button is clicked or needs to show a hint, it will check if something has been assigned to the Events property, and if it is, calls the necessary method.
Now, let's see how we would go and implement this using the new inline interfaces feature:
begin var b := new Button; var lCaptionWhenClicked := 'You clicked me!'; b.Events := new class IButtonDelegate(OnClick := method(b2: Button) begin b2.Caption := lCaptionWhenClicked; end, OnGetHoverHint := b2 -> 'This is the '+b2.Caption+' button'); end.
First you see we are using the extended new class syntax, which works symmetrical to new class as used for Anonymous Classes. In essence, this does create an anonymous class, one that implements the IButtonDelegate interface. Inside the parenthesis, we provide an implementation for some or all of the methods that are defined by the interface. These can be in anonymous method format (as OnClick shows in the example), as lambdas (as OnGetHoverHint illustrates), or they could use a reference to an existing method on the class, using the @ operator (OnClick := @HandleClick).
There are two things worth mentioning here: For one, just like regular anonymous methods, the interface implementations have full access to the surrounding code. The event handlers can access the local lCaptionWhenClicked variable; they could also access self or any members of the containing object. For another, while in this case we implement all (whopping two) members of the interface, it is not necessary to provide implementations for all members defined in the interface. You only need to implement the members you need, empty implementations will automatically be provided by the compiler for any method you omit.
Instead of providing the name of the interface as part of the new class syntax, it is also permissible to specify the name of an actual class (that implements the necessary interface), instead:
begin b.Events := new class BaseButtonDelegate(OnClick := method(b2: Button) begin ... end); end.
In this case, rather than creating a new class descending from Object and implementing the interface, the compiler will create a descendant from the specified class, in this case BaseButtonDelegate. This class will of course inherit all the functionality already present in the base class, but allow you to override select method implementations, as needed.
The Android APIs, in particular, make a lot of use of delegates where default implementations are provided and only a couple of methods need to be overridden to complete the delegate implementation.