Generic containers can be a time saver when having a item, and a strongly typed list of those items. It saves the repetitive coding of creating a new class with perhaps a TList
This was prompted by Deltic's answer, I wanted to provide an counter-example proving you can use generics for the animal feeding routine. (ie: Polymorphic Generic List)
First some background: The reason you can feed generic animals using a generic base list class is because you'll usually have this kind of inheritance:
TBaseList = class
// Some code to actually make this a list
end
TSpecificList = class(TBaseList)
// Code that reintroduces the Add and GetItem routines to turn TSpecificList
// into a type-safe list of a different type, compatible with the TBaseList
end
This doesn't work with generics because you'll normally have this:
TDogList = TList
end
TCatList = TList
end
... and the only "common ancestor" for both lists is TObject
- not at all helpful. But we can define a new generic list type that takes two class arguments: a TAnimal
and a TSpecificAnimal
, generating a type-safe list of TSpecificAnimal
compatible with a generic list of TAnimal
. Here's the basic type definition:
TCompatibleList = class(TObjectList)
private
function GetItem(i: Integer): T2;
public
procedure Add(A:T2);
property Item[i:Integer]:T2 read GetItem;default;
end;
Using this we can do:
TAnimal = class;
TDog = class(TAnimal);
TCat = class(TAnimal);
TDogList = TCompatibleList;
TCatList = TCompatibleList;
This way both TDogList and TCatList actually inherit from TObjectList
, so we now have a polymorphic generic list!
Here's a complete Console application that shows this concept in action. And that class is now going into my ClassLibrary for future reuse!
program Project23;
{$APPTYPE CONSOLE}
uses
SysUtils, Generics.Collections;
type
TAnimal = class
end;
TDog = class(TAnimal)
end;
TCat = class(TAnimal)
end;
TCompatibleList = class(TObjectList)
private
function GetItem(i: Integer): T2;
public
procedure Add(A:T2);
property Item[i:Integer]:T2 read GetItem;default;
end;
{ TX }
procedure TCompatibleList.Add(A: T2);
begin
inherited Add(T1(TObject(A)));
end;
function TCompatibleList.GetItem(i: Integer): T2;
begin
Result := T2(TObject(inherited Items[i]));
end;
procedure FeedTheAnimals(L: TObjectList);
var A: TAnimal;
begin
for A in L do
Writeln('Feeding a ' + A.ClassName);
end;
var Dogs: TCompatibleList;
Cats: TCompatibleList;
Mixed: TObjectList;
begin
try
// Feed some dogs
Dogs := TCompatibleList.Create;
try
Dogs.Add(TDog.Create);
FeedTheAnimals(Dogs);
finally Dogs.Free;
end;
// Feed some cats
Cats := TCompatibleList.Create;
try
Cats.Add(TCat.Create);
FeedTheAnimals(Cats);
finally Cats.Free;
end;
// Feed a mixed lot
Mixed := TObjectList.Create;
try
Mixed.Add(TDog.Create);
Mixed.Add(TCat.Create);
FeedTheAnimals(Mixed);
finally Mixed.Free;
end;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.