Okay, so my last article was pretty fun – you got a look at TVirtualInterface and how you can use it to implement any interface. Now, I want to be clear – the code in that article was demo code. That is, it was purely for illustrative purposes. I can’t think of any reason why you’d actually, in the real world, implement an interface that way. But after reading it, you should be able to see how TVirtualInterface works and how you can get it to do what you want.
Now, in that article I promised you that my next blog post on the topic would be an example of using TVirtualInterface in a more useful manner. But I guess I lied, because this blog post is about how you can create a slightly better version of TVirtualInterface.
So a while back, I wrote a post about getting you to “think generically” and how generics (or as I prefer to think of them, parameterized types) are useful in more ways than just collections and lists. Well, I got to looking at TVirtualInterface and I thought, “You know, here’s a class that actually requires type information about a given type, and in order for it to do anything useful, you have to give it a type in the constructor, so hmmm…….”. And you can guess where I went from there.
So, consider the following class declaration:
TVirtualInterfaceEx<T: IInvokable> = class(TVirtualInterface)
procedure DoInvoke(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue);
procedure DoInvokeImpl(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue); virtual; abstract;
This is a pretty simple descendent for TVirtualInterface. The most obvious thing is that it takes a parameterized type T which is constrained to be an interface descending from IInvokable. That enables you to explicitly declare what interface TVirtualInterfaceEx is going to implement. You should notice, too that TVirtualInterfaceEx is an abstract class, as the DoInvokeImpl method is abstract.
So, once you have the parameterized type, you know everything you need to implement the interface. As you know from the previous article, the thing you need to do is to provide an implementation of DoInvoke. So TVirtualInterfaceEx employs the technique I’ve described before whereby you implement the interface in the base class and provide a “real” implementation in a separate method invoked by the base class. So, the implementation looks like this:
inherited Create(TypeInfo(T), DoInvoke);
procedure TVirtualInterfaceEx<T>.DoInvoke(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue);
DoInvokeImpl(Method, Args, Result);
The constructor is pretty simple – it is parameter-less and calls a sibling constructor, passing in the TypeIinfo for your interface and the DoInvoke method which is of type TVirtualInterfaceInvokeEvent. The code for DoInvoke method simply calls the DoInvokeImpl method, which, because it is abstract, descendent classes must override.
Thus, to use this class, all you need to do is to descend from it and provide an interface as a parameterized type and an implementation for DoInvokeImpl. So, if we wanted to implement the IActuallyUseful interface from the previous example, all we need to do is:
TActuallyUsefulEx = class(TVirtualInterfaceEx<IActuallyUseful>)
procedure DoInvokeImpl(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue); override;
implementing DoInvokeImpl with the same code that was in the DoInvoke event of the TActuallyUseful class.
By the way, I should mention that the code for these demos on TVirtualInterface, including a sneak peek at the code for the next article, is available as part of my demo code project on BitBucket.
This isn’t anything really fancy, but I liked it because it simplified the process of creating virtual interface implementations and provided another example of a good use for parameterized types. I also like what I mentioned previously, that it clearly declares what interface it is implementing.
Anyway, I thought it was a cool little wrapper/descendent for the TVirtualInterface class. And I promise that the next blog post on this stuff will show an example of using it in a truly dynamic way.