Amazon.com Widgets TVirtualInterface: Next Steps

TVirtualInterface: Next Steps

By Nick at June 14, 2012 06:48
Filed Under: Delphi, Software Development

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:

type

  TVirtualInterfaceEx<T: IInvokable> = class(TVirtualInterface)
  protected
    procedure DoInvoke(Method: TRttiMethod;  const Args: TArray<TValue>; out Result: TValue);
    procedure DoInvokeImpl(Method: TRttiMethod;  const Args: TArray<TValue>; out Result: TValue); virtual; abstract;
  public
    constructor Create;
  end;

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:

constructor TVirtualInterfaceEx<T>.Create;
begin
  inherited Create(TypeInfo(T), DoInvoke);
end;

procedure TVirtualInterfaceEx<T>.DoInvoke(Method: TRttiMethod;  const Args: TArray<TValue>; out Result: TValue);
begin
  DoInvokeImpl(Method, Args, Result);
end;

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>)
  protected
    procedure DoInvokeImpl(Method: TRttiMethod;  const Args: TArray<TValue>; out Result: TValue); override;
  end;

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.

Comments (5) -

6/14/2012 10:26:21 AM #

While I like the idea of the IInvokable constraint to make sure you have the {$M+} switch at compiletime instead of some runtime exception telling you the method info is missing. But it limits its use for interfaces that don't inherit from IInvokable but have defined the {$M+}.

If you look at DSharp.Core.Dynamics.pas you can see how I used a generic descendant of TVirtualInterface.

Stefan Glienke Germany |

6/14/2012 12:10:18 PM #

Stefan --

Good point -- I'll make that change in the demo code.

And I will give DSharp.Core.Dynamics.pas a closer look.  It's very cool.

Nick

nick United States |

6/14/2012 6:29:23 PM #

This is pretty cool stuff, would you mind if I made a video of this and your previous blog posts?

Alister Christie New Zealand |

6/15/2012 6:10:32 PM #

Alister --

No, of course I wouldn't mind.  

I should have a third installment about TVirtualInterface this weekend.

nick United States |

6/23/2012 2:05:28 PM #

This strikes me as a good way of creating PLUGINS that could be dynamically loaded and unloaded at run-time.

David S United States |

Pingbacks and trackbacks (1)+

Comments are closed

My Book

A Pithy Quote for You

"We sleep safe in our beds because rough men stand ready in the night to visit violence on those who would do us harm."    –  George Orwell

Amazon Gift Cards

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.

Month List