Reputation: 2060
I tend to use Delphi's TStringList for text manipulation, so I write a lot of procedures/functions like:
var
TempList: TStringList;
begin
TempList:= TStringList.Create;
try
// blah blah blah do stuff with TempList
finally
TempList.Free;
end;
end;
It would be nice to cut out the creation and freeing for such a common utility class.
Since we now have records with methods, is it possible to wrap a Class like TStringList in a Record so I could just have:
var
TempList: TRecordStringList;
begin
// blah blah blah do stuff with TempList
end;
Upvotes: 4
Views: 2787
Reputation: 38703
If you are lucky enough to have upgraded to Delphi 2009 then check out Barry's work with smart pointers.
TSmartPointer<T: class> = record
strict private
FValue: T;
FLifetime: IInterface;
public
constructor Create(const AValue: T); overload;
class operator Implicit(const AValue: T): TSmartPointer<T>;
property Value: T read FValue;
end;
They are really cool, but require Generics and Anonymous methods. If you haven't upgraded to Delphi 2009, then do it now! Especially while they are offering their BOGO special. You also get Marco's Delphi Developer Handbook free just for downloading the trial. I already purchased a copy of it too.
Upvotes: 4
Reputation: 38703
There is another example already implemented in CC.
StringList is the same as TStringList except that it is a value type. It does not have to be created, destroyed or put within try/finally. This is done by the compiler for you. There are virtually no special performance penalties for these to work:
var strings: StringList; astr: string; begin strings.Add('test1'); strings.Add('test2'); aStr := string(strings); RichEdit.Lines.AddStrings(strings); end;
The code can be used as a template to wrap any TObject as a value class type.
It already has everything for a TStringList exposed for you.
Upvotes: 2
Reputation: 42152
It's possible. Create an interface that exposes the methods / objects you want:
type
IStringList = interface
procedure Add(const s: string); // etc.
property StringList: TStringList read GetStringList; // etc.
end;
Implement the interface, and have it wrap a real TStringList
:
type
TStringListImpl = class(TInterfacedObject, IStringList)
private
FStringList: TStringList; // create in constructor, destroy in destructor
// implementation etc.
end;
Then implement the record:
type
TStringListRecord = record
private
FImpl: IStringList;
function GetImpl: IStringList; // creates TStringListImpl if FImpl is nil
// returns value of FImpl otherwise
public
procedure Add(const s: string); // forward to GetImpl.Add
property StringList: TStringList read GetStringList; // forward to
// GetImpl.StringList
// etc.
end;
The fact that there's an interface inside the record means the compiler will handle reference counting automatically, calling _AddRef and _Release as copies get created and destroyed, so lifetime management is automatic. This works for objects that will never contain a reference to themselves (creating a cycle) - reference counting needs various tricks to get over cycles in the reference graph.
Upvotes: 15