AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Wie *schnell* auf Properties anhand eines String-Bezeichners zugreifen?

Wie *schnell* auf Properties anhand eines String-Bezeichners zugreifen?

Ein Thema von BlueStarHH · begonnen am 8. Jul 2019 · letzter Beitrag vom 9. Jul 2019
Antwort Antwort
Seite 2 von 2     12
peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
318 Beiträge
 
Delphi 10.3 Rio
 
#11

AW: Wie *schnell* auf Properties anhand eines String-Bezeichners zugreifen?

  Alt 8. Jul 2019, 15:13
Hallo,

ich habe ein Liste die mehrere simple Objekt enthält. Diese Objekt besitzen sehr viele Properties, um Daten zu speichern:

Delphi-Quellcode:
 TMyDataObj = class(TObject)
  private
    ...
  public
    property ValueA: string read ValueA write ValueA;
    property ValueB: string read ValueB write ValueB;
    property ValueC: string read ValueC write ValueC;
    ...
  end;
Ich benötige nun eine Funktion, der ich einen Feldnamen übergebe, anhand dessen dann der Wert einer bestimmten Property zurückgegeben werden soll. Beispielsweise GetFieldValue(ADataObj, 'MeinFeldX')

Mein Code dazu:

Delphi-Quellcode:
procedure GetFieldValue(ADataObj: TMyDataObj; const FieldName: string): string;
begin
  if AnsiSameText(FieldName, 'Wert1') then
    result := ADataObj.ValueA
  else if AnsiSameText(FieldName, 'MeinFeldX') then
    result := ADataObj.ValueB
  if AnsiSameText(FieldName, 'Feld7') then
    result := ADataObj.ValueC
end;
Wie man sieht brauche ich pro Property einen String-Vergleich. Der FieldName stimmt nie mit dem Namen der Propery überein. Wenn das ganze nun für viele Felder und sehr viele Objekte in einer verschachtelten Schleife aufgrufen wird, dauert die Ausführung zu lange. Da die String-Vergleiche für jedes Objekt wieder und wieder ausgeführt werden. Wie kann ich das schneller hinbekommen? Kann man die Verknüpfung vom String-Feldname zur Property nicht irgendwie speichern, nach dem sie das erste mal ermittelt wurde? Wenn ich so Objekte identifizieren wollte, würde ich diese in einem TDictionary<AObjectName,AObject> speichern. Das geht aber nicht mit properties oder doch?
Nicht direkt, aber es würde folgendermaßen gehen:

Definiere für jede Klasse deiner Datenobjekte einen enumerated type, nennen wir ihn mal TPropEnum, der für jede der betroffenen Properties einen Identifier enthält. Gib der Klasse eine Methode, die als Parameter Werte dieser Enumerierung bekommt und den Wert der zugeordneten Property zurückgibt (eventuell als Variant wenn die Properties unterschiedliche Typen haben können). Die Implementierung ist ein simples case-Statement, und das ist schnell.

Was Du dann noch brauchst ist ein Mapping der Feldnamen auf Werte des enumerated types. Die Quelle dafür kann ein simpler array [TPropEnum] of string sein. Dazu gehört dann ein TDictionary<string,TPropEnum>, das Du *einmal* aus dem Array initialisierst.

Damit kannst Du dann anhand des Dictionaries einen Feldnamen schnell auf einen Wert der Enumeration mappen und mit dem dann den Wert aus dem Objekt holen.

Das ist noch optimierbar, erfordert dazu aber eine Umarbeitung der Klassen. Du könntest die Daten nämlich direkt in einem Feld des Types array [TPropEnum] of variant speichern anstelle einzelner Felder pro Property, und für die Properties dann Getter und Setter implementieren, die auf den internen Array zugreifen. Siehe index keyword für Properties. Als Index kann auch ein enumerated type verwendet werden.

Eine Alternative gäbe es da noch, aber dafür müßten die Properties published sein, damit RTTI (die klassische Variante) für sie erzeugt wird. Dann könntest Du nämlich ein TDictionary<string,PPropinfo> bauen, um die Feldnamen mit den Properties zu verbinden. Mit den Helferroutinen aus der system.TypInfo Unit kann man den Wert einer Property mittels des zugehörigen PPropInfo extrahieren.
Peter Below
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
7.208 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#12

AW: Wie *schnell* auf Properties anhand eines String-Bezeichners zugreifen?

  Alt 9. Jul 2019, 07:08
Am schnellsten wäre, wenn du ein Dictionary mit dem Feldnamen und dem Pointer auf den jeweiligen Getter erstellst. Dazu noch ein bisschen Casting und eine Prise Generics, dann sollte so etwas in der Art möglich sein:
Code:
type
  TGenericGetter<T> = function: T of object;

FFields: TDictionary<string, Pointer>;

function TFieldAccess.Get<T>(const AObject: TObject; AFieldName: string; out AData: T): Boolean;
var
  FieldAddress: Pointer;
  Getter: TGenericGetter<T>;
  GetterMethod: TMethod;
begin
  Result := FFields.TryGet(AFieldName, FieldAddress);
  if Result then
  begin
    GetterMethod.Data := AObject;
    GetterMethod.Code := FieldAddress;
    AData := TGenericGetter<T>(GetterMethod);
  end;
end;
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:34 Uhr.
Powered by vBulletin® Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2019 by Daniel R. Wolf