It adds a pair of items: an entry in the TStringList.Strings list, and a matching TObject in the TStringList.Objects list.
This allows you to, for instance, store a list of strings that supply a name for the item, and an object that is the class containing the matching item.
type
TPerson=class
FFirstName, FLastName: string;
FDOB: TDateTime;
FID: Integer;
private
function GetDOBAsString: string;
function GetFullName: string;
published
property FirstName: string read FFirstName write FFirstName;
property LastName: string read FLastName write FLastName;
property DOB: TDateTime read FDOB write FDOB;
property DOBString: string read GetDOBAsString;
property FullName: string read GetFullName;
property ID: Integer read FID write FID;
end;
implementation
{TPerson}
function TPerson.GetDOBAsString: string;
begin
Result := 'Unknown';
if FDOB <> 0 then
Result := DateToStr(FDOB);
end;
function TPerson.GetFullName: string;
begin
Result := FFirstName + ' ' + FLastName; // Or FLastName + ', ' + FFirstName
end;
var
PersonList: TStringList;
Person: TPerson;
i: Integer;
begin
PersonList := TStringList.Create;
try
for i := 0 to 9 do
begin
Person := TPerson.Create;
Person.FirstName := 'John';
Person.LastName := Format('Smith-%d', [i]); // Obviously, 'Smith-1' isn't a common last name.
Person.DOB := Date() - RandRange(1500, 3000); // Make up a date of birth
Person.ID := i;
PersonList.AddObject(Person.LastName, Person);
end;
// Find 'Smith-06'
i := PersonList.IndexOf('Smith-06');
if i > -1 then
begin
Person := TPerson(PersonList.Objects[i]);
ShowMessage(Format('Full Name: %s, ID: %d, DOB: %s',
[Person.FullName, Person.ID, Person.DOBString]));
end;
finally
for i := 0 to PersonList.Count - 1 do
PersonList.Objects[i].Free;
PersonList.Free;
end;
This is clearly a contrived example, as it's not something you'd really find useful. It demonstrates the concept, though.
Another handy use is for storing an integer value along with a string (for instance, showing a list of items in a TComboBox or TListBox and a corresponding ID for use in a database query). In this case, you just have to typecast the integer (or anything else that is SizeOf(Pointer)) in the Objects array.
// Assuming LBox is a TListBox on a form:
while not QryItems.Eof do
begin
LBox.Items.AddObject(QryItem.Fields[0].AsString, TObject(QryItem.Fields[1[.AsInteger));
QryItems.Next;
end;
// User makes selection from LBox
i := LBox.ItemIndex;
if i > -1 then
begin
ID := Integer(LBox.Items.Objects[i]);
QryDetails.ParamByName('ItemID').AsInteger := ID;
// Open query and get info.
end;
In the case of storing things other than an actual TObject, you don't need to free the contents. Since they're not real objects, there's nothing to free except the TStringList itself.
TList<T>supersedes this technique.WideStringinstead ofUnicodeString). If the "official standard code" is not using generics nor even cleaned code... as most of us, they just want to make it right! So clean only on rewrite, not cleaning when you want it to be just cleaner or fashioned. Adding test-driven methods does make sense. Using generics does not per se. "What for" is better than "how".