Explicit interface implementations in Delphi 8
published: Wed, 14-Jan-2004 | updated: Thu, 27-Oct-2005
So, there I was, merrily writing an article about Delphi for .NET, using my C# and .NET knowledge to impart words of wisdom. When, bam, I hit a roadblock.
Now, you must understand that this roadblock was yet one of several that I'd been hitting this afternoon. The others, however, turned out to be somewhat easy to circumvent. This latest one deserves at least a mild rebuke for Borland R&D, but a rebuke that could turn out to be far stronger.
In C#, you can implement an interface explicitly, rather than in the normal, implicit way. Here's an example of an implicit interface implementation with ICloneable:
class Foo : ICloneable { object Clone() { Console.WriteLine("In ICloneable's Clone()"); return CloneMe(); } private Foo CloneMe() { return new Foo(); } }
(Ignore the awkward-looking call to CloneMe() for the moment, I'll need it later.) To call Clone() in code to get a clone of a Foo object, I would have to write this:
Foo f = new Foo(); Foo f2 = (Foo) f.Clone();
This is a little naff to say the least: every time I call Clone() I have to cast the result to a Foo object. Wouldn't it be better to be able to write this:
Foo f = new Foo(); Foo f2 = f.Clone();
and get rid of the cast? With C# you can, of course, by writing an explicit interface implementation of ICloneable:
class Foo : ICloneable { object ICloneable.Clone() { Console.WriteLine("In ICloneable's Clone()"); return CloneMe(); } public Foo Clone() { Console.WriteLine("In Foo's Clone()"); return CloneMe(); } private Foo CloneMe() { return new Foo(); } }
Notice a couple of things about this version of the Foo class. First, there are two Clone() methods, and they both call our private method CloneMe(). Second, the first Clone() method implements ICloneable's Clone() — it explicitly says so — and it returns a simple object. The second one implements a Clone() method, certainly, but it actually does something more: it's a type-safe Clone() since it returns a Foo object and not just a generic object. The first Clone() method can only be called if you cast the object to ICloneable first:
Foo f = new Foo(); object f3 = ((ICloneable) f).Clone();
This is a boon to writing type-safe container classes (those who know my background are now saying, ah, so that's where he's going), and its the only easy way to implement them until generics comes along in Visual Studio 2004.
So what's this got to do with Delphi for .NET? Well, Delphi for .NET does not support this idiom. You cannot write an explicit interface implementation in this way. So if I write a class in Delphi that implements ICloneable:
type Foo = class(TObject, ICloneable) private protected public function Clone : TObject; end;
I have to use the "implicit" version. My Clone method will always return a TObject (an alias for System.Object for those who don't know) and never the type-safe version.
Actually this isn't so bad for return types, but suppose I have a container interface (ooh, let's say, IList) and I wish to implement it. In C#, I can write:
class FooList : IList { int IList.Add(object obj) { // blah } public int Add(Foo f) { // blah } ... }
and have a fairly foolproof type-safe way of making sure that I can only add Foo objects to a FooList. (Agreed, the type-safety goes away if I cast a FooList object to an IList, but in reality the users of my FooList will be adding Foos.) With Delphi for .NET, I'm lost, there's no easy way to achieve the same level of type-safety. Therein lies my roadblock: my article was about getting type-safety when implementing interfaces in Delphi 8.