I read with interest Nick Hodges blog on Why You Should Be Using Interfaces and since I'm already in love with interfaces at a higher level in my coding I decided to look at how I could extend this to quite low levels and to investigate what support for this existed in the VCL classes.
A common construct that I need is to do something simple with a TStringList, for example this code to load a small text file list into a comma text string:
var
MyList : TStrings;
sCommaText : string;
begin
MyList := TStringList.Create;
try
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
finally
MyList.Free;
end;
end;
It would seem a nice simplification if I could write with using MyList as an interface - it would get rid of the try-finally and improve readability:
var
MyList : IStrings;
//^^^^^^^
sCommaText : string;
begin
MyList := TStringList.Create;
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
end;
I can't see an IStrings defined though - certainly not in Classes.pas, although there are references to it in connection with OLE programming online. Does it exist? Is this a valid simplification? I'm using Delphi XE2.
Since
TStrings
is an abstract class, an interface version of it wouldn't provide much. Any implementer of that interface would surely be aTStrings
descendant anyway, because nobody would want to re-implement all the thingsTStrings
does. I see two reasons for wanting aTStrings
interface:Automatic resource cleanup. You don't need a
TStrings
-specific interface for that. Instead, use theISafeGuard
interface from the JCL. Here's an example:To protect multiple objects that should have the same lifetime, use
IMultiSafeGuard
.Interoperation with external modules. This is what
IStrings
is for. Delphi implements it with theTStringsAdapter
class, which is returned when you callGetOleStrings
on an existingTStrings
descendant. Use that when you have a string list and you need to grant access to another module that expectsIStrings
orIEnumString
interfaces. Those interfaces are clunky to use otherwise — neither provides all the thingsTStrings
does — so don't use them unless you have to.If the external module you're working with is something that you can guarantee will always be compiled with the same Delphi version that your module is compiled with, then you should use run-time packages and pass
TStrings
descendants directly. The shared package allows both modules to use the same definition of the class, and memory management is greatly simplified.