13.06.2011

Delphi and Generics

In this first developer-themed post, I am going to describe some basic stuff, before I delve into some real-world applications.

Delphi 2009 brought Generics to the Delphi-World. And since then, they are found in all of my Applications. Especially Generics.Collections are heavily used by me.

Before Generics.Collections I had to implement a TList for each Class I would like to store a variable amount of Objects. (Or I could use Contnrs.TObjectList and cast to TAnimal everytime I need one, but I don't find that very desirable)
interface

uses
  Classes;

type
  TAnimal = class
  private
    fName : string;
  public
    property Name : string read fName write fName;
  end;
  
  TAnimalList = class (TList)
  private
  procedure putAnimal(Index : integer; Value : TAnimal);
  function getAnimal(Index : integer) : TAnimal;
  public  
    property Items [Index : integer] : TAnimal read getAnimal write setAnimal;
  end;
  
implementation

procedure TAnimalList.putAnimal(Index : integer; Value : TAnimal);
begin
  inherited put(Index, Value);
end;

function TAnimalList.getAnimal(Index : integer) : TAnimal;
begin
  result := (TAnimal) inherited get(Index);
end;

And this is just for TAnimal, if there are any other classes, I would have to copy&paste the code for the List and replace the data types.

With Generics.Collections the same thing looks like this:
interface

uses
  Generics.Collections;

type
  TAnimal = class
  private
    fName : string;
  public
    property Name : string read fName write fName;
  end;

  TAnimalList = TList<TAnimal>

And I don't even need the alias "TAnimalList", I could use "TList<TAnimal>" instead.

Also, it is not limited to Classes, it can hold every datatype like integer, double, boolean and even records. In my case it is even better to use TObjectList<TAnimal>, which automatically frees its containing objects if it is freed.

Since all the Generics.Collectionshave an Enumerator the following is possible:

var
  fAnimals : TList<TAnimal>

procedure init;
var
  a : TAnimal;
begin
  for a in fAnimals do
  begin
    a.DoSomething;
  end;
end;

Generics.Collections has more then some simple Lists up its sleeve. There are also TQueue, which uses FIFO and TStack which uses LIFO to store its contents.

TDictionary is a little different, because its taking two type parameters. One for the key, one for the Value.
A case that I often stumble upon is to store (and recieve) some object or value for a specific string. And then I always use a TDictionary<String,TValue>

There's also a TObjectDictionary, which takes care of memory of the keys, the values or even both.
procedure init;
var
  ownsKeys, ownsValues, ownsBoth : TObjectDictionary<TKey, TValue>
begin
  ownsKeys := TObjectDictionary<TKey, TValue>.Create([doKeys]);
  ownsValues := TObjectDictionary<TKey, TValue>.Create([doValues]);
  ownsBoth := TObjectDictionary<TKey, TValue>.Create([doKeys, doValues]);
end;

That's it for today.

Keine Kommentare: