Generic Interfaces in Delphi

Most of the examples I’ve seen of Generics in Delphi, use classes containing a generic type. However, while working on a personal project, I decided I wanted an Interface containing a generic type.

The project uses an in-process publish/subscribe mechanism, and I wanted a subscriber to have a separate Receive method for each event type, rather than a single method which contained a big case statement with branches for each event type. Equally, however, I didn’t want to have to define an interface type for each event type. What I wanted was to have a generic Subscriber interface, that took the event type as a parameter.

However, I had no idea if I could define a generic Interface, let alone implement one. Even assuming I could do both of those things, would Delphi be able to resolve the correct Receive method to invoke? Only one way to find out….

NB: In this example, I’ve stripped out most of the pub/sub stuff, just leaving the pieces needed to show the generic interfaces. I’ll write about the other pieces over the next few posts.

First, I implemented a few sample events. The contents of these are not that interesting:

TSomeEvent = class
  // other stuff 
end;

TSomeOtherEvent = class
  // other stuff 
end;

Then, I defined a generic interface

ISubscriber = interface
  procedure Receive(Event : T); 
end;

This is the interface that will need to be implemented by my subscribers in order to receive events of a particular type. Note, the type of event is set up as a generic type T.

Then, my subscribers need to implement an interface for each Event type they want to receive, however because it is a generic interface, it’s pretty straight-forward:

TMySubscribingObject = class(TInterfacedObject, ISubscriber, ISubscriber)
protected
  procedure Receive(Event : TSomeEvent); overload; 
  procedure Receive(Event : TSomeOtherEvent); overload; 
end;

Note there’s no definition of a ISomeEventSubscriber and a ISomeOtherEventSubscriber interface, we can just use ISubscriber<T> and pass the type in in-place. We just need to implement the necessary overloaded Receive events.

You can see the rest of the code in the associated test project, but the above shows you the main aim. Implementing multiple interfaces, each with a strongly-typed Receive event, without actually having to define each of those interfaces.

So, does it work? Well, at my first attempt, no, it didn’t. No matter what type of Event I passed in, or via what interface, it always invoked the last Receive method.

dunit_generic_interfacesRule of life #37: If it’s a choice between Malcolm having screwed up or the Delphi compiler architects, it’s probably Malcolm’s fault.

Yes, my bad. Barry Kelly eventually pointed out the error of my ways. I’d originally defined the generic Interface with a GUID. Habit, really. However, this meant that both ISubscriber<TSomeEvent> and ISubscriber<TSomeOtherEvent>, and indeed any other interface I defined off this generic interface, would have the same GUID. This combined with the fact that I was using an “as” cast to get the Interface reference from the TMySubscribingObject instance, confused Delphi into always giving me the same Interface reference.

Drop the GUID and drop the “as”, and it all worked beautifully.

In future posts I’ll show the other side of the equation, the Publisher, as well as the Event Broker in the middle. One other nice side-effect of indicating the events a class is interested in, using interfaces in this way, is that the event broker can simply inspect the interfaces implemented by a subscriber to know which events they are interested in subscribing to.

9 Comments

  • Twitter Comment


    RT @malcolmgroves: Generic Interfaces in Delphi [link to post] (nice article)

    Posted using Chat Catcher

  • Twitter Comment


    [link to post]

    Posted using Chat Catcher

  • […] Groves on generic interfaces in Delphi 2009 2009 July 28 by admin Malcolm Groves has a nice post on leveraging generic interfaces to implement multicast events in Delphi 2009. Even with the […]

  • Twitter Comment


    Generic interfaces – Delphi 2009/2010
    [link to post]

    Posted using Chat Catcher

  • Twitter Comment


    Interfaces genéricas – Delphi 2009/2010
    [link to post]

    Posted using Chat Catcher

  • Hi Mal,

    Great article! I remember skimming this article in the middle of last year and it just went way over my head. Now that I got Delphi 2010, some of those pieces are s-l-o-w-l-y falling into place.

    Started doing my own event aggregator using some patterns and examples in C# but I’m so far just experimenting what I can and can’t do in Delphi.

    I am also trying to use types instead of strings to define my events. I went from trying it with generics then failed and tried it with class references and now, reading your article, I gonna try it again with generics!

    I know this article was posted ages ago but I was just wondering if you are still planning to do post on your Publisher and EventBroker?

    Fingers-crossed that you have some time to think about it between your busy schedules!

    Cheers,
    Colin

  • Your article is interesting. I have a related question…

    Can you use generics such that a class solver method could accept a global function or a class method as a parameter?

    I have described the full problem on this page:

    http://dl.dropbox.com/u/18869118/Articles/RootFinder/index.htm

  • Good article. But I still wonder how you query interfaces from class. Since they have no GUIDs, all standard operations like as, is, Supports can’t work. Of course, we could try to make explicit casting if we sure this class supports specific interface. But the advantage of generics is that we shouldn’t know in advance with which type or class we will work.

    If you can, please, publish your Publisher and EventBroker.

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>