Einzelnen Beitrag anzeigen

blackdrake

Registriert seit: 21. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#1

"Array" mit Strings als Indizes (Zuordnungstabelle

  Alt 15. Mär 2009, 20:45
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:
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;
Ein Anwendungsbeispiel:

Delphi-Quellcode:
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;
Anmerkung: Die Indizes sind natürlich case sensitive, also x.GetValue('A') ist etwas anderes als x.GetValue('a').

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:
var
  i: integer;
begin
  for i := 0 to List.Count - 1 do
  begin

  end;
end;
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:

Delphi-Quellcode:
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;
Die Verwendung muss dann so aussehen:

MeinInteger := TInteger(x.GetValue('MeineZahl')).Content; 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:

Delphi-Quellcode:
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
  ...
Für Verbesserungsvorschläge bin ich natürlich immer offen!

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat