Men pyt med det! Måske nogle af mine læsere ved bedre end mig, og kan oplyse mig om hvorvidt "For in"-operatoren fandtes i Delphi 2005 og eller 2006?
Først vil jeg lige komme med en opfriskning af, hvordan operatoren ser ud i Delphi:
var s : String; begin for s in Memo1.Lines do Memo2.Lines.Add(s); end;
Et lidt tænkt eksempel, men dog et eksempel :o) Jeg antager de fleste er bekendt med den ovenstående syntaks, og således vil jeg ikke gå i yderligere detaljer med dens betydning. Derimod vil jeg her vise, hvordan man selv kan implementere operatoren på sine egne klasser. Typisk lister.
Skal man have en typestærk liste i Delphi 2007, er man nød til selv at lave en nedarvning fra TObjectList, i stil med den her:
unit MyListU;
interface
uses
Contnrs;
type
TMyClass = class
private
FSomeString: String;
FSomeInteger: Integer;
procedure SetSomeInteger(const Value: Integer);
procedure SetSomeString(const Value: String);
public
property SomeInteger : Integer read FSomeInteger write SetSomeInteger;
property SomeString : String read FSomeString write SetSomeString;
end;
TMyList = class(TObjectList)
private
function GetItem(Index: Integer): TMyClass;
procedure SetItem(Index: Integer; const Value: TMyClass);
public
property Items[Index: Integer]: TMyClass read GetItem write SetItem; default;
end;
implementation
{ TMyClass }
procedure TMyClass.SetSomeInteger(const Value: Integer);
begin
FSomeInteger := Value;
end;
procedure TMyClass.SetSomeString(const Value: String);
begin
FSomeString := Value;
end;
{ TMyList }
function TMyList.GetItem(Index: Integer): TMyClass;
begin
Result := inherited GetItem(Index) as TMyClass;
end;
procedure TMyList.SetItem(Index: Integer; const Value: TMyClass);
begin
inherited SetItem(Index, Value);
end;
end.
Når så man vil bruge sin liste og vil iterere sig i gennem den, kan man fx. gøre det sådan her:procedure TMainForm.BtnTest1Click(Sender: TObject); var MyList : TMylist; MyObject : TMyClass; i : Integer; begin MyList := TMylist.Create; //Todo: Tilføj elementer for I := 0 to MyList.Count - 1 do begin MyObject := MyList[i]; //Todo: brug MyObject end; FreeAndNil(MyList); end; |
Der findes også en anden mulighed: At implementere sin egen "for in"-operator. Jeg tager udgangspunkt i min liste fra før, og har nu lavet nogle tilføjelser, som er dokumenteret i koden.
unit MyListU; interface uses Contnrs; type { Først skal man lave et par Forward declarations, for at få det hele til at compilere. } TMyClass = class; TMyList = class; { Så skal du bugge en klasse op over den nedenstående skabelon. Det er en helt triviel øvelse som vist ikke kræver det store :o) } TMyListEnumerator = class private FIndex: Integer; FList: TMyList; public constructor Create(AList: TMyList); function GetCurrent: TMyClass; function MoveNext: Boolean; property Current: TMyClass read GetCurrent; end; TMyClass = class private FSomeString: String; FSomeInteger: Integer; procedure SetSomeInteger(const Value: Integer); procedure SetSomeString(const Value: String); public property SomeInteger : Integer read FSomeInteger write SetSomeInteger; property SomeString : String read FSomeString write SetSomeString; end; TMyList = class(TObjectList) private function GetItem(Index: Integer): TMyClass; procedure SetItem(Index: Integer; const Value: TMyClass); public { GetEnumerator er en metode som Delphi kalder når man itererer sig igennem listen, med en "for in"-operator. } function GetEnumerator: TMyListEnumerator; property Items[Index: Integer]: TMyClass read GetItem write SetItem; default; end; implementation { TMyClass } procedure TMyClass.SetSomeInteger(const Value: Integer); begin FSomeInteger := Value; end; procedure TMyClass.SetSomeString(const Value: String); begin FSomeString := Value; end; { TMyList } function TMyList.GetEnumerator: TMyListEnumerator; begin //Der skal bare retuneres en ny instans. Delphi nedlægger den selv efter brug. Result := TMyListEnumerator.Create(Self); end; function TMyList.GetItem(Index: Integer): TMyClass; begin Result := inherited GetItem(Index) as TMyClass; end; procedure TMyList.SetItem(Index: Integer; const Value: TMyClass); begin inherited SetItem(Index, Value); end; { TMyListEnumerator } constructor TMyListEnumerator.Create(AList: TMyList); begin inherited Create; FIndex := -1; FList := AList; end; function TMyListEnumerator.GetCurrent: TMyClass; begin Result := FList[FIndex]; end; function TMyListEnumerator.MoveNext: Boolean; begin Result := FIndex < FList.Count - 1; if Result then Inc(FIndex); end; end.
Således er jeg nu klar til at bruge den operator jeg lige har implementeret:
procedure TMainForm.BtnTest2Click(Sender: TObject); var MyList : TMylist; MyObject : TMyClass; begin MyList := TMylist.Create; //Todo: Tilføj elementer for MyObject in MyList do begin //Todo : brug MyObject end; FreeAndNil(MyList); end;
Jens Borrisholt
Hej Jens
SvarSletFint Blog indlæg.
For eksemplets skyld har du fuldstændig ret. Dette er en måde at implementere en typestærk liste.
Givet de Delphi versioner vi benytter i dag (jeg kan heller ikke huske hvornår tingene kom til... Men måske Delphi 2010), ville jeg nok vælge en generics liste som myList : Tobjectlist;
Men så får man ikke lejlighed til at implementere sin egen for in ;)
Men tak for indlægget.
Jens Fudge
Hej Jens
SvarSletBåde ja og nej. Du har fuldstændig ret i Delphi 2010 og senere vil man klart vælge en Generist liste og dermed vil der blive meget mindre kode.
Men den der med at du ikke får lov til at lave din gene operator er ikke helt korrekt, hvis ud vil have mulighed for flere operatorer skal man selv implementere dem.
Men tak for indput. Så fik jag da lige en ide til en post mere :o)