mandag den 31. oktober 2011

TList.Sort();


Jeg formoder at alle bekendt med, at Delphi fra version 2009 har generiske typer. Herunder generiske lister.

Helt kort: Hvis man skal bruge en type stærk liste af en bestemt type - det kan være en simpletype, en klasse eller en record - så skal man:

1) Uses Generics.Collections
2) Erklære en variable:   List : TList<TMyType>;
3) Kalde dens constructor:    List := TList<TMyType>.Create;

... Og så bruger man den bare som en almindelig typestærk liste.

Et lille hurtigt eksempel kunne se således ud:

procedure Demo;
var
  i: Integer;
  PointList: TList<TPoint>;
begin
  PointList := TList<TPoint>.Create;

  for i := 1 to 10 do
    PointList.Add(Point(Random(100), Random(100)));

  PointList.Sort;

  FreeAndNil(PointList);
end;


MEEEN! Nu er det behovet opstår for SELV at bestemme, hvordan listen skal sorteres.
I Delphi kan man ikke bruge lighedsoperatoren til at sammenligne to generiske elementer i listen med. I stedet for implementerer man sin egen TComparer og giver den med - enten til listens constructor eller til sorteringen. 

I det følgende vælger jeg at sortere mine punkter efter afstanden til centrum, men først lige en lille hjælper, som giver mig et par metoder på min TPoint: 

TPointHelper = record helper for TPoint
  public
    function DistanceToCenterSquare: Integer; inline;
    function DistanceToCenter: Extended; inline;
  end;

{ TPointHelper }

function TPointHelper.DistanceToCenter: Extended;
begin
  Result := Sqrt(DistanceToCenterSquare);
end;

function TPointHelper.DistanceToCenterSquare: Integer;
begin
  Result := X * X + Y * Y;
end;


Og så skal vi have vore TComparer implementeret: 


type
  TPointComparer = class(TComparer<TPoint>)
    function Compare(const Left, Right: TPoint): Integer; override;
  end;

{ TPointComparer }

function TPointComparer.Compare(const Left, Right: TPoint): Integer;
begin
  Result := Left.DistanceToCenterSquare - Right.DistanceToCenterSquare;
end;




Tilbage er der blot at tage vores nye klasse i brug:

procedure Demo2;
var
  i: Integer;
  PointList: TList<TPoint>;
begin
  PointList := TList<TPoint>.Create(TPointComparer.Create);

  for i := 1 to 10 do
    PointList.Add(Point(Random(100), Random(100)));

  PointList.Sort;

  FreeAndNil(PointList);
end;




BEMÆRK: TList er ikke trådsikker. Ej heller TList<T>. Hvordan løser man så det? Man pakker tilgangen ind i en TMonitor, og styrer tilgangen ad den vej. Men det må blive genstand for et senere blog indlæg :-)

Jens Borrisholt

Ingen kommentarer:

Send en kommentar