![]() |
"Array" mit Strings als Indizes (Zuordnungstabelle
Wer viel mit PHP zu tun gehabt hat, wird bei Delphi sicherlich dynamische Arrays vermissen, die Strings als Indizes haben. Da ich gerade an einem Projekt arbeite, bei dem der Benutzer einem Objekt einen konkreten Name-Identifier geben soll, habe ich mal eine Klasse erstellt, die einen String ein TObject zuweist. Dieses TObject kann man dann mit konkreten Instanzen einer TObject-Ableitung belegen (z.B. einer TImage-Instanz).
Delphi-Quellcode:
Ein Anwendungsbeispiel:
interface
uses Contnrs; type TEasyObjectManager = class(TObject) published constructor Create; destructor Destroy; override; public function GetValue(Name: string): TObject; procedure SetValue(Name: string; Value: TObject); function ValueExists(Name: string): boolean; procedure DeleteValue(Name: string); function Count: integer; function GetValueById(Index: integer): TObject; private List: TObjectList; end; implementation type TNameObjectPair = class(TObject) Name: string; Value: TObject; end; (*** TEasyObjectManager ***) constructor TEasyObjectManager.Create; begin inherited; // OwnsObject ist false. Das heißt, die Objekte, die wir verwalten, // werden nicht freigegeben. List := TObjectList.Create(false); end; destructor TEasyObjectManager.Destroy; begin if Assigned(List) then List.Free; inherited; end; function TEasyObjectManager.GetValue(Name: string): TObject; var i: integer; begin result := nil; for i := 0 to List.Count-1 do begin if TNameObjectPair(List.Items[i]).Name = Name then begin result := TNameObjectPair(List.Items[i]).Value; break; end; end; end; procedure TEasyObjectManager.SetValue(Name: string; Value: TObject); var i: integer; n: TNameObjectPair; found: boolean; begin found := False; for i := 0 to List.Count-1 do begin if TNameObjectPair(List.Items[i]).Name = Name then begin TNameObjectPair(List.Items[i]).Value := Value; found := true; break; end; end; if not found then begin UniqueString(Name); n := TNameObjectPair.Create; n.Name := Name; n.Value := Value; List.Add(n); end; end; function TEasyObjectManager.ValueExists(Name: string): boolean; var i: integer; begin result := false; for i := 0 to List.Count-1 do begin if TNameObjectPair(List.Items[i]).Name = Name then begin result := true; break; end; end; end; procedure TEasyObjectManager.DeleteValue(Name: string); var i: integer; begin for i := 0 to List.Count-1 do begin if TNameObjectPair(List.Items[i]).Name = Name then begin List.Delete(i); break; end; end; end; function TEasyObjectManager.Count: integer; begin result := List.Count; end; function TEasyObjectManager.GetValueById(Index: integer): TObject; begin result := List.Items[Index].Value; end;
Delphi-Quellcode:
Anmerkung: Die Indizes sind natürlich case sensitive, also x.GetValue('A') ist etwas anderes als x.GetValue('a').
var
x: TEasyObjectManager; begin x := TEasyObjectManager.Create; try x.SetValue('foo', Form1); // foo wird angelegt ShowMessage(TForm(x.GetValue('foo')).Caption); x.SetValue('foo', Button1); // foo wird überschrieben ShowMessage(TButton(x.GetValue('foo')).Caption); finally x.Free; end; end; Count() und GetValueById() können verwendet werden, um alle Elemente der Zuordnungstabelle durchzugehen. Dies wäre dann mit einer For-Schleife möglich (In PHP würde man für diesen Zweck eine Foreach-Schleife verwenden).
Delphi-Quellcode:
Diese Klasse müsste jedoch modifiert werden, wenn ihr Strings, Integer, Booleans oder Floats anstelle von Objekten verwalten wollt. Das wäre aber in sofern ungünstig, da dann viel rudundanter Code vorliegen würde. Ich habe mir jetzt von Java noch eine Interessante Funktionalität abgeguckt. Dort ist zwar der String ein abstrakter Datentyp, also eine Klasse, jedoch ist auch ein Integer bei Java ein einfacher Datentyp (int). Der Vorteil in Java: Weise ich ein int einem Object zu, wird dieser automatisch in eine Integer-Containerklasse gewandelt (toll!). In Delphi könnt ihr also folgende Helferklassen für meine Zuordnungsklasse verwenden:
var
i: integer; begin for i := 0 to List.Count - 1 do begin end; end;
Delphi-Quellcode:
Die Verwendung muss dann so aussehen:
type
TString = class(TObject) Content: string; end; TInteger = class(TObject) Content: integer; end; TFloat = class(TObject) Content: float; end; TBoolean = class(TObject) Content: boolean; end;
Delphi-Quellcode:
Wer natürlich ausschließlich mit Integers o.ä. arbeiten will, kann natürlich meinen Code editieren und alle TObject umwandeln. Verwaltet ihr aber Strings, müssen die Values mit UniqueString() speichertechnisch einzigartig gemacht werden:
MeinInteger := TInteger(x.GetValue('MeineZahl')).Content;
Delphi-Quellcode:
Für Verbesserungsvorschläge bin ich natürlich immer offen!
procedure TEasyStringManager.SetValue(Name: string; Value: string);
var i: integer; n: TNameStringPair; found: boolean; begin found := False; UniqueString(Value); // <-- Bei einer String-String Zuordnungstabelle ist diese Zeile zusätzlich nötig! for i := 0 to List.Count-1 do ... Gruß blackdrake |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Das gibt es bereits, mit viel besserer Laufzeit:
![]() |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Delphi-Quellcode:
Oder besser:
TStringList
Zitat:
|
Re: "Array" mit Strings als Indizes (Zuordnungstab
Hallo. Das mit den Hash-Tabellen schaue ich mir mal an und schaue, ob diese Klasse alles kann, was meine auch kann.
Aber TStringList kann doch die Funktionalität nicht wirklich ersetzen, oder? Eine TStringList ordnet Integer -> String/Object zu. Ich will aber String -> Object. |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Eine TStringList verwaltet Strings :roll: , optional kann man jedem ein Object hinzufügen.
Die Hashed-Variante ist in der Regel zu bevorzugen, jedoch nicht bei älteren Delphis verfügbar. |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Zitat:
Bei optimierter Implementierung kann aber eine eigene Klasse trotzdem sinnvoll sein. Dies ist eventuell das Interessante bei einer eigenen Implementierung. ;-) Die Hashvariante oder eine andere Implementierung mit Hashes ist aber meistens noch besser, das ist sicher ebenfalls richtig. |
Re: "Array" mit Strings als Indizes (Zuordnungstab
... und kann auch mit Strings als Index arbeiten, wenn man sich auf das Format Name=Wert einlässt. Aber wie die TStrings das intern handeln, kann einem ja egal sein.
Also - mal abgesehen von der THashStringList - klappt die String-Als-Index Sache von Haus aus mit TStringList. Siehe dazu [oh]TStrings.Values[][/oh]. |
Re: "Array" mit Strings als Indizes (Zuordnungstab
![]() |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Hallo.
Danke für eure Antworten. Ich habe jetzt folgende Variante für TStringList und THashedStringList geschrieben. Dadurch wird der Code auch gleich viel schlanker.
Delphi-Quellcode:
Gruß
uses
IniFiles; type TEasyObjectManager = class(TObject) published constructor Create(CaseSensitive: boolean); destructor Destroy; override; public function GetValue(Name: string): TObject; procedure SetValue(Name: string; Value: TObject); function ValueExists(Name: string): boolean; procedure DeleteValue(Name: string); function Count: integer; function GetValueById(Index: integer): TObject; function GetNameById(Index: integer): string; private List: THashedStringList; // Alternativ: TStringList end; (*** TEasyObjectManager ***) constructor TEasyObjectManager.Create(CaseSensitive: boolean); begin inherited Create; List := THashedStringList.Create; // Ich bin der Meinung, man sollte das vorher definieren und nicht mittendrin ändern List.CaseSensitive := CaseSensitive; end; destructor TEasyObjectManager.Destroy; begin if Assigned(List) then List.Free; inherited; end; function TEasyObjectManager.GetValue(Name: string): TObject; var i: integer; begin i := List.IndexOf(Name); if i <> -1 then begin result := List.Objects[i]; end else begin result := nil; end; end; procedure TEasyObjectManager.SetValue(Name: string; Value: TObject); var i: integer; begin i := List.IndexOf(Name); if i <> -1 then begin List.Objects[i] := Value; end else begin UniqueString(Name); // Notwendig? List.AddObject(Name, Value); end; end; function TEasyObjectManager.ValueExists(Name: string): boolean; begin result := List.IndexOf(Name) <> -1; end; procedure TEasyObjectManager.DeleteValue(Name: string); var i: integer; begin i := List.IndexOf(Name); if i <> -1 then begin List.Delete(i); end; end; function TEasyObjectManager.Count: integer; begin result := List.Count; end; function TEasyObjectManager.GetValueById(Index: integer): TObject; begin result := List.Objects[Index]; end; function TEasyObjectManager.GetNameById(Index: integer): string; begin result := List.Strings[Index]; end; blackdrake |
Re: "Array" mit Strings als Indizes (Zuordnungstab
Ok, das ist jetzt ein Wrapper für eine Klasse mit ein paar extra Methoden, um nicht jedesmal den Index mit den Funktionen dafür zu ermitteln, sondern das direkt da rein zu bauen? :gruebel: Wäre es nicht sinnvoller, wenn man die Klasse einfach ableitet und wenn man sowas braucht, das schnell hinzuzufügen, auch könnte das mit dem case sensitive sonst schwer werden?
Nebenbei sind auch die Abfragen, ob Einträge existieren unschön, entweder sie sollten dann angelegt werden, oder bei Abfragen sollte eine Exception geworfen werden, um zu signaliesieren, dass es den Eintrag nicht gibt, weil man nil ja auch zuweißen könnte.
Delphi-Quellcode:
Ich nicht, sowas sollte in eine Property.
// Ich bin der Meinung, man sollte das vorher definieren und nicht mittendrin ändern
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:59 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz