Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Property via AsString;AsInteger;AsBoolean;.. laden (https://www.delphipraxis.net/169970-property-via-asstring%3Basinteger%3Basboolean%3B-laden.html)

-=ZGD=- 23. Aug 2012 09:31

Property via AsString;AsInteger;AsBoolean;.. laden
 
Servus,

ich stehe vor einem kleinen Problem und komm leider auch trotz Suche nicht auf die richtigen Stichwörter.
Ich bin mir sicher, dieses Problem wurde schon öfter mal angesprochen.

Problem

Ich habe eine Konfigurationsklasse, in welcher wie gesagt Einstellungen auf Vorrat gehalten werden.
Das Lesen/Schreiben via
Delphi-Quellcode:
property Items[aKey: String]: String read GetValue write SetValue;
in dem einfach der Schlüssel übergeben wird funktioniert problemlos.

Ich möchte jetzt aber nicht ständig Typenumwandlungen machen und daher ist meine Fragen:

Wie kann ich es lösen, dass der Aufruf
Delphi-Quellcode:
myConfig.Items['dbport'].AsInteger
genügen würde und ich den Port als
Delphi-Quellcode:
Integer
erhalten?

Über Links und Hilfestellung jeder Art bin ich dankbar.

Auch wenn wie hier die Antwort von mkinzler korrekt erscheint, mein Problem scheinbar beschreibt, hilft es absolut nicht weiter...

Ich danke vielmals.

bepe 23. Aug 2012 09:45

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Warum hilft dir die Antwort von mkinzler nicht weiter? Es ist nämlich die einzige Lösung die genau zu dem gewünschten Verhalten führt.

Deine Items müssten als Objekte in einem Array oder in einer Liste verwaltet werden. Dann könntest du über
Delphi-Quellcode:
myConfig.Items['dbport'].WasAuchImmer
auf die Methoden und Propertys zugreifen. Und du müsstest nur noch pro Datentyp eine Property mit entsprechender Getter Methode bereitstellen.

Alternativ könntest du auch weitere Propertys in deiner Config Klasse machen. Ungefähr so
Delphi-Quellcode:
property ItemsAsInteger[aKey: String]: Integer read GetIntegerValue write SetIntegerValue;
.

Aber du wirst zu jedem Datentyp eine Property samt Getter Methode schreiben müssen, welche sich dann um die Typumwandlung kümmert.

-=ZGD=- 23. Aug 2012 09:49

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von bepe (Beitrag 1179472)
Warum hilft dir die Antwort von mkinzler nicht weiter? Es ist nämlich die einzige Lösung die genau zu dem gewünschten Verhalten führt.

Weil "ja" mir nicht ausreichend war ;-)

Zitat:

Zitat von bepe (Beitrag 1179472)
Deine Items müssten als Objekte in einem Array oder in einer Liste verwaltet werden. Dann könntest du über
Delphi-Quellcode:
myConfig.Items['dbport'].WasAuchImmer
auf die Methoden und Propertys zugreifen. Und du müsstest nur noch pro Datentyp eine Property mit entsprechender Getter Methode bereitstellen.

Das wäre eigentlich genau mein Plan. Dann muss ich mit einer ObjectList arbeiten?

Zitat:

Zitat von bepe (Beitrag 1179472)
Alternativ könntest du auch weitere Propertys in deiner Config Klasse machen. Ungefähr so
Delphi-Quellcode:
property ItemsAsInteger[aKey: String]: Integer read GetIntegerValue write SetIntegerValue;
.

Aber du wirst zu jedem Datentyp eine Property samt Getter Methode schreiben müssen, welche sich dann um die Typumwandlung kümmert.

Hmm, auf diese Methode bin ich noch gar nicht gekommen. Der Implementierungsaufwand wäre in beiden Fällen der Gleiche...
Ich danke dir - einstweilen..

bepe 23. Aug 2012 10:00

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von -=ZGD=- (Beitrag 1179473)
Weil "ja" mir nicht ausreichend war ;-)

Das hab ich mir fast gedacht :-D Wollte eigentlich wissen wie ausführlich ich antworten sollte... Aber wenn du erst einmal weiter kommst, ist ja alles gut.

Die Variante mit den Objekten wäre meiner Meinung nach "schöner". Aber ist auch mit mehr Tipparbeit verbunden.

Die ObjectList wäre wohl eine gute Lösung. Aber es gibt (zumindest je nach Delphi Version) mehrere Ansätze. Da deine Items Namen haben, würde sich z.B. auch ein
Delphi-Quellcode:
TDictionary
anbieten.

-=ZGD=- 23. Aug 2012 10:01

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
So sieht es jetzt bei mir aus

Ich habe ein
Delphi-Quellcode:
record
über alle Einträge:
Delphi-Quellcode:
type
  RConfig = record
    Key: String;
    Value: String;
  end;
Daraus einen eigenen Typ, als dynamisches Array

Delphi-Quellcode:
type
  TConfigList = array of RConfig;
Welches Verwendung in der Klassendeklaration
Delphi-Quellcode:
type
  TConfig = class(TObject)
  private
    Con: TZConnection;
    Query: TZQuery;

    fNewDB: Boolean;

    fList: TConfigList;

    function GetValue(aID: String): String;
    procedure SetValue(aID: String; aValue: String = '');
    function getItemCount: Integer;
  public
    constructor create;

    property IsNew: Boolean read fNewDB;
    property Items[aKey: String]: String read GetValue write SetValue;
    property Count: Integer read getItemCount;
    procedure ReadConfig;
    function Exists(aKey: String): Boolean;
    function save: Boolean;
  end;
in der privaten Variable
Delphi-Quellcode:
fList
Anwendung findet.

Wie würde jetzt meine Deklaration aussehen, wenn ich die
Delphi-Quellcode:
Items
als Objekt handhaben möchte.

Meine Idee wäre dann

Delphi-Quellcode:
type
  TConfigItem = class(TObject)
  private
    function GetAsInteger: Integer;
    function GetAsString: String;
  public
    property AsInteger: Integer read GetAsInteger;
    property AsString: String read GetAsString;
  end;
Woraus sich ergeben würde, dass
Delphi-Quellcode:
Items[aKey: String]: TConfigItem
wird.
Allerdings mit welchem
Delphi-Quellcode:
read
?

bepe 23. Aug 2012 10:18

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von -=ZGD=- (Beitrag 1179475)
So sieht es jetzt bei mir aus

Ich habe ein
Delphi-Quellcode:
record
über alle Einträge:
Delphi-Quellcode:
type
  RConfig = record
    Key: String;
    Value: String;
  end;
Daraus einen eigenen Typ, als dynamisches Array

Delphi-Quellcode:
type
  TConfigList = array of RConfig;

Soweit, so gut. Du brauchst jedoch nicht Objekt und Record. Du verwendest XE2? Dann kannst du in deinem Record Propertys deklarieren, wie du es auch in einem Objekt machen würdest.
Zitat:

Zitat von -=ZGD=- (Beitrag 1179475)
Welches Verwendung in der Klassendeklaration
Delphi-Quellcode:
type
  TConfig = class(TObject)
  private
    Con: TZConnection;
    Query: TZQuery;

    fNewDB: Boolean;

    fList: TConfigList;

    function GetValue(aID: String): String;
    procedure SetValue(aID: String; aValue: String = '');
    function getItemCount: Integer;
  public
    constructor create;

    property IsNew: Boolean read fNewDB;
    property Items[aKey: String]: String read GetValue write SetValue;
    property Count: Integer read getItemCount;
    procedure ReadConfig;
    function Exists(aKey: String): Boolean;
    function save: Boolean;
  end;
in der privaten Variable
Delphi-Quellcode:
fList
Anwendung findet.

Wie würde jetzt meine Deklaration aussehen, wenn ich die
Delphi-Quellcode:
Items
als Objekt handhaben möchte.

Meine Idee wäre dann

Delphi-Quellcode:
type
  TConfigItem = class(TObject)
  private
    function GetAsInteger: Integer;
    function GetAsString: String;
  public
    property AsInteger: Integer read GetAsInteger;
    property AsString: String read GetAsString;
  end;

Wie erwähnt, du kannst die Funktionen und Propertys einfach ins Record verschieben und brauchst die Klasse nicht mehr. Aber ansonsten korrekt umgesetzt (wenn du nur lesend auf die Werte zugreifen möchtest).

Zitat:

Zitat von -=ZGD=- (Beitrag 1179475)
Woraus sich ergeben würde, dass
Delphi-Quellcode:
Items[aKey: String]: TConfigItem
wird.
Allerdings mit welchem
Delphi-Quellcode:
read
?

An der Deklaration von Items ändert sich, außer dem Typ, nichts. Also so wie du es gemacht hast*. Im
Delphi-Quellcode:
read
änderst du auch nur den Typ und greifst natürlich auf dein
Delphi-Quellcode:
TConfigList
array zu.

*Wenn du das Objekt entfernst und nur mit dem Record arbeitest, sehe Items so aus
Delphi-Quellcode:
Items[aKey: String]: RConfig

s.h.a.r.k 23. Aug 2012 10:53

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Schau dir dazu vielleicht auch mal die Operator-Überladung an, im speziellen Implicit. Damit bräuchtest du dann dieses AsString oder AsInteger nicht. Der Konvertierung würde dann alles automatisch funktionieren.

Besser für Refactoring wäre es, wenn du entsprechende Properties nutzen würdest. So handhabe ich das ganze -- momentan gibts den Code nur in VB.Net, portiere den aber bald nach Delphi:
Delphi-Quellcode:
// Habe die Property mal aufs Minimum reduziert
TXmlConfigurationNodeAttribute = class(TAttribute)
public
  property Path : string read GetPath;
end;

TBaseConfiguration = abstract class
protected
  FXmlPathToPropertyIndex : IDictionary<string, TRttiProperty>;
 
  // Diese Methode baut einen Index aus Pfad-zu-Property
  // Wertpaaren auf, welcher dann in der LoadConfiguration
  // genutzt wird
  procedure ReadXmlProperties();
public
  // Diese Methode liest aus dem XML-Node die entsprechenden
  // Daten aus und setzt die gelesenen Werte via RTTI auf die
  // entsprechenden Properties. Natürlich wird dabei eine
  // Konvertierung der Datentypen vorgenommen.
  procedure LoadConfiguration(XmlNode: IXmlNode);
end;

// Die Methode ist nur schnell hingeschrieben, die Idee soll
// rüberkommen!
procedure TBaseConfiguration.LoadConfiguration(XmlNode: IXmlNode);
begin
  for PathPropertyPair in FXmlPathToPropertyIndex
  begin
    Path := PathPropertyPair.Key;
    Property := PathPropertyPair.Value;
    Value := GetValueFromXml(XmlNode, Path);
    Property.SetValue(Self, Convert(Value, Property));
  end;
end;

// So, was bringt das nun? Je Anwendung brauche ich dann nur noch
// eine abgeleitete Klasse schreiben, die minimalen Code enthält,
// aber dennoch dafür sorgt, dass alles passt und dazu auch noch
// wunderbar typsicher ist.
TMyConfiguration = class(TBaseConfiguration)
public
  [XmlConfigurationNode('ftp-server/host')]
  property Host : string read FHost write SetHost;

  [XmlConfigurationNode('ftp-server/port')]
  property Port : Integer read FPort write SetPort;
end;
Vielleicht findest die Idee ja interessant :)

Thom 23. Aug 2012 10:57

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Wie wäre es mit dem Typ Variant als Ergebnis? Dann übernimmt Delphi die komplette Typ-Konvertierung (dort, wo es geht) und man spart sich viel Schreibarbeit:
Delphi-Quellcode:
type
  TMyClass = class
  private
    procedure SetItem(const Name: String; const Value: Variant);
    function GetItem(const Name: String): Variant;
  public
    property Items[const Name: String]: Variant read GetItem write SetItem;
  end;

-=ZGD=- 23. Aug 2012 10:59

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Danke für dein Hilfe bepe.

Auf Grundlage deiner Informationen bin ich jetzt soweit vorangeschritten:

Delphi-Quellcode:
type
  RConfig2 = record
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
  public
    property Key: String read fKey write fKey;
    property Value: Variant write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;
Die Implementierung der GET/SET

Delphi-Quellcode:
function RConfig2.GetAsString;
begin
  result := STRING(fValue);
end;

function RConfig2.GetAsInteger;
var
  val: Integer;
begin
  if TryStrToInt(fValue, val) then
  begin
    result := val
  end
  else
    raise Exception.Create('Konvertierungsfehler - Kein INTEGER');
end;

Daraus ergab sich eine neue Liste
Delphi-Quellcode:
type
  TConfigList2 = array of RConfig2;

Was im Objekt an sich zu folgenden Änderungen führt:

Delphi-Quellcode:
type
  TConfig = class(TObject)
  private
    ...
    fList: TConfigList;
    fList2: TConfigList2;

    ...
    function GetVariantValue(aID: String): RConfig2;
    procedure SetVariantValue(aID: string; aValue: Variant);

    ...
  public
    constructor create;

    ...
    property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
    ...
  end;
Delphi-Quellcode:
GetVariantValue
sieht nun bei mir so aus (ja, noch keine Hashtable)

Delphi-Quellcode:
function TConfig.GetVariantValue(aID: string): RConfig2;
var
  i: Integer;
begin
  ReadConfig;
  for i := low(fList2) to high(fList2) do
  begin
    if uppercase(fList2[i].Key) = uppercase(aID) then
    begin
      Result := fList2[i];
      break;
    end;
  end;
end;
Ich dachte mir, ich nehme
Delphi-Quellcode:
Variant
als Typ für die Value, denn somit bin ich da flexibel...eher ungut?

Woran ich jetzt allerdings scheitere ist die Implementierung von
Delphi-Quellcode:
SetVariantValue
.

Der Compiler sagt
Code:
Inkompatible Typen
.

Delphi-Quellcode:
procedure Tconfig.SetVariantValue(aID: string; aValue: Variant);
begin

end;
Den Versuch, einfach ein
Code:
RConfig2
zu übergeben ist ebenfalls gescheitert.
Wo seh ich den Wald vor lauter Bäumen nicht? Ist die Implementierung in den Grundzügen so korrekt?

-=ZGD=- 23. Aug 2012 11:02

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von Thom (Beitrag 1179479)
Wie wäre es mit dem Typ Variant als Ergebnis? Dann übernimmt Delphi die komplette Typ-Konvertierung (dort, wo es geht) und man spart sich viel Schreibarbeit:
Delphi-Quellcode:
type
  TMyClass = class
  private
    procedure SetItem(const Name: String; const Value: Variant);
    function GetItem(const Name: String): Variant;
  public
    property Items[const Name: String]: Variant read GetItem write SetItem;
  end;

Hallo Thom,

das würde funktionieren, ist aber insofern unpraktisch, dass ich gern vordefinierte Werte zurückgeben möchte.

Wenn ich beispielsweise einen Port (INTEGER) zuweise und die
Code:
Value
leer ist, beschmeißt mich Delphi natürlich mit der korrekten Fehlermeldung
Code:
"" ist kein gültiger Integer-Wert
. Das würde ich umgehen wollen und einfach von vorn herein
Code:
0
übergeben.

s.h.a.r.k 23. Aug 2012 11:12

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Hast meinen Post oben schon gesehen?

Default-Werte solltest du im Konstruktor setzen. Dafür ist er da.

Und warum denn bitte Variant nehmen? Die ganze Typsicherheit geht verloren und du bekommst erst zur Laufzeit Probleme, da der Compiler die Typen nicht schon beim Compilieren prüfen kann.

Thom 23. Aug 2012 11:17

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
@-=ZGD=-:

Gut, dann müßtest Du natürlich vorher den Inhalt des Variant-Wertes testen:
Delphi-Quellcode:
  if VarIsType(Items['xxx'],varInteger)
    then IntegerWert:=Items['xxx']
    else IntegerWert:=IntegerDefault;
Willst Du diesen Test innerhalb Deines Objektes unterbringen, ist die Verwendung von Variant ungünstig und ich würde an Deiner Stelle auch Records nehmen.

Zitat:

Zitat von s.h.a.r.k (Beitrag 1179482)
Und warum denn bitte Variant nehmen? Die ganze Typsicherheit geht verloren und du bekommst erst zur Laufzeit Probleme, da der Compiler die Typen nicht schon beim Compilieren prüfen kann.

Dafür gibt es Tests... Und es gibt Sprachen, die bauen ausschließlich auf Variant auf (z.B. JavaScript).

s.h.a.r.k 23. Aug 2012 11:19

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Warum es erst zur Laufzeit krachen lassen, wenn mir schon der Compiler sagen kann, dass das was schief laufen wird? :gruebel: Läuft doch nur darauf hinaus, dass ich jeden Käse abfragen muss, bevor ich dann die eigentliche Variable nutzen kann. Und den Vorteil von den Variants sehe ich dann einfach nicht, tut mir Leid. Zudem erzeugst du so eher mehr Code und Unsicherheit, was es doch nicht brauch.

Zudem stell dir mal die Situation vor, dass ein Kollege deinen Code nutzen will und nicht weiß, was er denn für Daten bekommt. Ist der Wert von der "Variable" Index nun vom Type string oder Integer? Oder gar ganz was anderes. Was soll er denn mit Variant anfangen können? Erst mal die komplette Doku lesen, obwohl IntelliSense ihm sagen könnte "Hey, Index ist vom Typ string".
Zitat:

Dafür gibt es Tests... Und es gibt Sprachen, die bauen ausschließlich auf Variant auf (z.B. JavaScript).
Nur weil es Sprachen gibt, die darauf aufbauen -- wobei ich nicht wirklich weiß, wie JS das handhabt -- heißt es ja auch noch lange nicht, dass es gut ist. JavaScript und PHP haben mich oft genug mit diesem varianten Gehabe gestört. Wenn ich auf einmal aus einem string eine Klasse generieren kann, ist das vielleicht schon recht nett, aber man läuft echt in viele Probleme rein. Typsicherheit bringt auf jedenfall mehr Aufbau, sichert aber eben auch sehr viel zu, auf das man sich verlassen kann.

Thom 23. Aug 2012 11:25

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Ich verstehe Dein Problem mit Variant nicht. Dafür gibt es, wie schon geschrieben, entsprechende Tests. Da "kracht" gar nichts.
Außerdem wird ja keiner gezwungen, den Typ Variant zu verwenden. Er hat - wie alle anderen Herangehensweisen auch - Vor- und Nachteile.

Nachtrag
Da Du Deinen Beitrag inzwischen geändert hast, muß ich auch noch etwas ergänzen:
Da ganze läuft wieder auf "meine Variante ist viel besser als Deine" heraus und reiht sich wunderbar in die ganzen sinnlosen Diskussionen wie "FreeAndNil ist schlecht", "globale Variablen sind böse", "wer ohne Entwurfsmuster arbeitet hat keine Ahnung" ein. Hängen wir also noch ein "Variant ist böse" dran.
Und damit bin ich weg...

s.h.a.r.k 23. Aug 2012 11:31

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Ich sage doch nur, dass aus meiner Sicht, Variants nicht der Typsicherheit vorgezogen werden sollte -- es gibt ja in der Zwischenzeit auch nicht um sonst die Generics.

Klar gibt es Tests, aber die werden erst zur Laufzeit ausgeführt und nicht zur Compilezeit. Und es ist wohl schöner eine einfache Zuweisung zu haben, als dass ich mehrere Abfragen einbauen muss. Klar die Konvertierung beim Einlesen einer Konfiguration ist ein Mehraufwand, den man bei Variants nicht hat, aber den Variants muss ich ständig kritisch gegenüber stehen und prüfen, ob da auch was valides drin steht. Jedes mal wenn ich darauf zugreife. Was glaubst du, wie es denn deinem Kollegen geht, der deinen Code nutzt, wie im Beispiel oben erwähnt?

Ich habe bisher echt noch nie so wirklich einen Variant genutzt, vor allem ab Delphi2010 und den Generics. Nicht nur aufgrund der Typsicherheit, sondern auch aufgrund der Performance.

-=ZGD=- 23. Aug 2012 11:35

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Hab ich gesehen Shark, etwas zu spät.

Hmm, mit RTTI möchte ich eigentlich nicht arbeiten, frag mich nicht warum, aber ich schau mir das in der Freizeit gern mal an.

Naja, Variant hält mir einfach die Flexibilität offen.
Für mich macht es im Moment mehr Sinn, wenn ich einfach sagen kann
Delphi-Quellcode:
myConfig.Items['iwas'].AsBoolen
und ich dann schon fertig meine Konvertierung, whatever gemacht habe.

Der Hintergrund ist total simpel: Ich habe eine SQLite-3-Datenbank in welcher eine Tabelle
Code:
config
ist mit den Feldern
Code:
key, value
.

Code:
value
kann jeden beliebigen Wert annehmen.

Steht da jetzt -1 drin, kann ich genauso mit
Code:
.AsInteger
, als auch mit
Code:
.AsBoolean
Abfragen gestalten.
Das ist einfach n Stück weit flexibel, ohne dass ich andere Entwickler mit strikten Typen bewerfen muss.

Code:
.AsInteger, .AsString, .AsBoolean, ...
sind einfach vom Query abgekupfert.

Es funkioniert wie ich es haben wollte bereits lesender Weise. Mir fehlt nur noch das
Code:
SET
.
Hierzu eine Idee?

PS Ich bin von Variant im Moment nicht abzubringen ;-)

s.h.a.r.k 23. Aug 2012 11:53

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Aus welchem Grund will ich einen Boolean-Wert auf -1 abfragen? Was hat das denn mit Flexibilität zu tun? Es ist entweder ein Boolen oder ein Integer oder ein string. Gut, der einzige Vorteil, den ich gerade sehe ist, dass ich dem Variant auch eien Null-Wert geben kann, das lasse ich mir eingehen. Dafür gibts aber einen generischen Nullable-Typ, entweder einen selbst geschriebenen oder gleich der vom Spring-Framework.

Finde es jedenfalls etwas seltsam bei jedem Aufruf wissen zu müssen, welchen Datentyp, denn gerade vorliegen soll, in der Hoffnung, dass dann auch das passende im Variant drin steht. Klar, wenn das System nicht groß ist und nicht viele Einstellungen hat, dann ists noch überschaubar. Aber ständig die Doku neben dran liegen zu haben und nachschauen zu müssen, welche Variable von welchem Typen ist... Und das wird dir, wenn du die Software wartest, irgendwann mal passieren, da du nicht jeden Tag den Code in den Fingern hast.

Wo liegt das Problem beim Setter? Füge dem Record ein SetValue() hinzu und gut is.

-=ZGD=- 23. Aug 2012 12:03

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179494)
Aus welchem Grund will ich einen Boolean-Wert auf -1 abfragen? Was hat das denn mit Flexibilität zu tun? Es ist entweder ein Boolen oder ein Integer oder ein string. Gut, der einzige Vorteil, den ich gerade sehe ist, dass ich dem Variant auch eien Null-Wert geben kann, das lasse ich mir eingehen. Dafür gibts aber einen generischen Nullable-Typ, entweder einen selbst geschriebenen oder gleich der vom Spring-Framework.

Finde es jedenfalls etwas seltsam bei jedem Aufruf wissen zu müssen, welchen Datentyp, denn gerade vorliegen soll, in der Hoffnung, dass dann auch das passende im Variant drin steht. Klar, wenn das System nicht groß ist und nicht viele Einstellungen hat, dann ists noch überschaubar. Aber ständig die Doku neben dran liegen zu haben und nachschauen zu müssen, welche Variable von welchem Typen ist... Und das wird dir, wenn du die Software wartest, irgendwann mal passieren, da du nicht jeden Tag den Code in den Fingern hast.

Wo liegt das Problem beim Setter? Füge dem Record ein SetValue() hinzu und gut is.

Du frägst keinen Boolean auf -1 ab, aber das wäre in dem Falle ein TRUE und ist selbiges Feld als Integer -1 kannst du genauso damit arbeiten und zum Beispiel Werte einfach negieren, wenn du es *-1 nimmst.
Jetzt nicht den Kopf zerbrechen, warum und woher.. :)

Pass auf:

Code:
KEY | VALUE
dbhost | localhost
dbport | 3306
dbuser | root
...
Das wäre jetzt ein Auszug aus der Config-Tabelle.

Bisher wurde alles als String ausgelesen.
Nun kann ich meiner Port-Komponente keinen String zuweisen. Daher die Intention
Code:
.AsInteger
So, die Doku muss der Entwickler sowieso bei sich haben, damit er weiß, welche Konfigurationfelder er eigentlich hat, da ist´s egal, dass er noch schaut und liest: Typ.INTEGER.

Delphi-Quellcode:
property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
SetVariantValue ist ein Prozedur
Delphi-Quellcode:
procedure SetVariantValue(aID: String; aValue: Variant);
.

Ich hab auch schon versucht,
Code:
RConfig2
zu übergeben, aber ich scheitere...

Thom 23. Aug 2012 12:04

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
@s.h.a.r.k:

Die Performance habe ich noch nicht verglichen. Ob es zwischen dem zusätzlichen Code, der von Compiler für Variant erzeugt wird und dem, der durch eigene Konvertierungen entsteht, signifikante Unterschiede gibt, müßte man einmal testen.
Aber wie schon geschrieben: Variant ist kein Wundermittel und hat seine Grenzen - wie jede andere Lösung auch. Generics sind ebenfalls kein Allheilmittel. Ich denke, es kommt immer auf den Programmierer an, eine für den konkreten Fall optimierte Lösung zu finden.

Ich bin absolut kein Freund von JavaScript - auch wenn/obwohl/gerade weil ich mich intensiv damit beschäftig habe. Aber es gibt dort Konstruktionen, die sich nur mit extrem großem Aufwand oder eben gar nicht mit einer typsicheren Sprache wie Delphi umsetzen lassen. Anders herum werden Entwickler, die es nicht gewohnt sind, sich mit dem Typ von Parametern und Variablen auseinanderzusetzen, sehr schwer tun, Delphi einzusetzen. Typ oder Nicht-Typ - das ist alles eine Frage der Gewöhnung. Es aber gleich als schlecht abzutun, weil man es selbst nicht gewohnt ist, ist aber auch nicht der richtige Weg...

-=ZGD=- 23. Aug 2012 12:13

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von Thom (Beitrag 1179499)
@s.h.a.r.k:

Die Performance habe ich noch nicht verglichen. Ob es zwischen dem zusätzlichen Code, der von Compiler für Variant erzeugt wird und dem, der durch eigene Konvertierungen entsteht, signifikante Unterschiede gibt, müßte man einmal testen.
Aber wie schon geschrieben: Variant ist kein Wundermittel und hat seine Grenzen - wie jede andere Lösung auch. Generics sind ebenfalls kein Allheilmittel. Ich denke, es kommt immer auf den Programmierer an, eine für den konkreten Fall optimierte Lösung zu finden.

Ich bin absolut kein Freund von JavaScript - auch wenn/obwohl/gerade weil ich mich intensiv damit beschäftig habe. Aber es gibt dort Konstruktionen, die sich nur mit extrem großem Aufwand oder eben gar nicht mit einer typsicheren Sprache wie Delphi umsetzen lassen. Anders herum werden Entwickler, die es nicht gewohnt sind, sich mit dem Typ von Parametern und Variablen auseinanderzusetzen, sehr schwer tun, Delphi einzusetzen. Typ oder Nicht-Typ - das ist alles eine Frage der Gewöhnung. Es aber gleich als schlecht abzutun, weil man es selbst nicht gewohnt ist, ist aber auch nicht der richtige Weg...

Performance ist mir vollkommen egal, da nur beim Start der Anwendung die lokale Konfiguration gelesen wird.
Ob da nun 200ms oder 1000ms dauert, damit können die Anwendung und auch ich leben.

Ich suche einfach nach einer schneller, guten und dauerhaften Lösung.
Die Klasse wird einmal geschrieben und dann nie wieder im Code angefasst, weil sie eben genau das tut, was sie soll: Konfigurationen lesen und schreiben.

Ich arbeite sowohl mit Delphi, PHP und auch Javascript und das schon seit Jahren. Es gibt nur eben Themen (RTTI, Generics) mit denen ich mich noch nicht beschäftigt habe, dass aber zu gegebenem Zeitpunkt und in Ruhe tun werde.
Wohlmöglich sehe ich dann die Config-Klasse anders, warum auch nicht.

Das alles sein Für und Wider hat, dagegen ist nichts einzuwenden. Letztlich entscheidet das Gutdünken des Entwicklers darüber, wie er es lösen möchte.

Ich möchte letzten Endes einfach die 230 Codezeilen so haben, dass diese funktionieren :-P

Iwo Asnet 23. Aug 2012 12:14

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Wir arbeiten hier seit Jahren mit dem Vorschlag von Thom (Variants).

Unsere Lösung ist so banal, das wir auf Typensicherheit, OOP, CleanCode usw. an der Stelle verzichten. Die Unit ist 186 Zeilen lang und implementiert einen bösen Singleton namens 'Setup'. Fertig.

Wir verwenden die Eigenschaften 'AsInteger', 'AsString', 'AsBoolean', um komfortabler auf Daten zugreifen zu können (nötig ist das nicht):

Delphi-Quellcode:
ValueAsVariant := Setup['MySetting'];
ValueAsInteger := Setup.AsInteger['MyIntegerSetting'];
ValueAsBoolean := Setup.AsBoolean['MyBooleanSetting'];
...
Wir hatten bisher nie die Muße, über den Sinn zu diskutieren. Die Unit wurde in 30 min zusammengebaut und funktioniert.

Nein, und unsere Anwendungen haben keine 1000 Einstellungen. Höchstens 20.

Wer mit Metadaten arbeitet (und hier haben wir welche), sollte sich mit den Variants vertraut machen.

-=ZGD=- 23. Aug 2012 12:18

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von Iwo Asnet (Beitrag 1179504)
Wir arbeiten hier seit Jahren mit dem Vorschlag von Thom (Variants).

Unsere Lösung ist so banal, das wir auf Typensicherheit, OOP, CleanCode usw. an der Stelle verzichten. Die Unit ist 186 Zeilen lang und implementiert einen bösen Singleton namens 'Setup'. Fertig.

Wir verwenden die Eigenschaften 'AsInteger', 'AsString', 'AsBoolean', um komfortabler auf Daten zugreifen zu können (nötig ist das nicht):

Delphi-Quellcode:
ValueAsVariant := Setup['MySetting'];
ValueAsInteger := Setup.AsInteger['MyIntegerSetting'];
ValueAsBoolean := Setup.AsBoolean['MyBooleanSetting'];
...
Wir hatten bisher nie die Muße, über den Sinn zu diskutieren. Die Unit wurde in 30 min zusammengebaut und funktioniert.

Nein, und unsere Anwendungen haben keine 1000 Einstellungen. Höchstens 20.

Wer mit Metadaten arbeitet (und hier haben wir welche), sollte sich mit den Variants vertraut machen.

Meine Worte :-)
Bei mir werden es auch nicht mehr als 50 Werte werden.

Ich finde diese Lösung eben einfach und alltagstauglich. Man muss nicht alles auf die Spitze treiben.

@Shark: Ich habe doch einen Setter im Record..wo finde ich meinen Fehler?

s.h.a.r.k 23. Aug 2012 12:48

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von -=ZGD=- (Beitrag 1179506)
@Shark: Ich habe doch einen Setter im Record..wo finde ich meinen Fehler?

Jo, habe übersehen, dass es sich um eine reine write-Property bei Value handelt. Sowas gibts bei mir nie, daher ging von einem read aus :stupid:

Zitat:

Zitat von -=ZGD=- (Beitrag 1179506)
So, die Doku muss der Entwickler sowieso bei sich haben, damit er weiß, welche Konfigurationfelder er eigentlich hat, da ist´s egal, dass er noch schaut und liest: Typ.INTEGER.

Guter Code benötigt (fast) keine Doku. Ich persönlich finde es praktischer, wenn mir IntelliSense sagt, welche Optionen ich denn zur Auswahl habe, anstatt in der Doku nachschauen zu müssen. Dann kann ich alles an Ort und Stelle auswählen nud sehe auch beim Compilieren, ob es knallt, wenn Änderungen an der Configuration-Klasse vorgenommen werden.

Folgendes Beispiel:
Delphi-Quellcode:
Port := Config.GetValue('port').AsInteger;
Port := Config.GetValue('Port').AsInteger;
// Was von den beiden ist richtig? Klar, man könnte in der GetValue()
// Methode noch auf Lower- oder UpperCase umwandeln, aber es kann
// hier leicht knallen.

// Wenn ich aber nun folgendes habe, erscheint in meinem Code eine
// Auflistung aller optionen, die die Klasse Config anbietet. Grob
// mal mit IntelliSense skizziert :)
Port := Config.+----------------+
               | MySQL         |
               | Printer       |
               | UserSpecific  |
               +----------------+

// Klar, muss die Configuration-Klasse entsprechend angepasst werden,
// sodass die Auswahl kommt. Aber so kann der Programmierer sich sehr
// leicht "durchhangeln", bis er das hat, was er sucht:
Port := Config.MySQL.Port;
Davon losgelöst kann man eben auch bzgl. den Variants argumentieren, was ich schon habe. Wer aber die Variants-Lösung bevorzugt, dem sei gesagt, dass ich nichts dagegen habe. Ich beschreibe hier nur den Weg, den ich bevorzugen würde -- aus besagten Gründen :wink:

Zitat:

Das alles sein Für und Wider hat, dagegen ist nichts einzuwenden. Letztlich entscheidet das Gutdünken des Entwicklers darüber, wie er es lösen möchte.
Was anderes habe ich nie behauptet :) Viele Wege führen ans Ziel...

bepe 23. Aug 2012 12:50

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von -=ZGD=- (Beitrag 1179480)
Delphi-Quellcode:
type
  RConfig2 = record
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
  public
    property Key: String read fKey write fKey;
    property Value: Variant write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;

Ich würde im Record die Setter Methoden einfügen, nicht in der Config Klasse. Dann kannst du einfach
Delphi-Quellcode:
...AsInteger := 4711
tippen, anstatt ein Record übergeben zu müssen.

Zitat:

Zitat von -=ZGD=- (Beitrag 1179480)
Delphi-Quellcode:
type
  TConfig = class(TObject)
  private
    ...
    fList: TConfigList;
    fList2: TConfigList2;

    ...
    function GetVariantValue(aID: String): RConfig2;
    procedure SetVariantValue(aID: string; aValue: Variant);

    ...
  public
    constructor create;

    ...
    property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
    ...
  end;

Hier ist der Getter ausreichend. Aber wenn schon ein Setter da ist, dann definitiv mit RConfig2 arbeiten nicht mit Variant. Auch wenn ich nichts gegen Variant habe (doch eigentlich schon :-D), es macht hier einfach keinen Sinn.

s.h.a.r.k 23. Aug 2012 13:00

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Wenn du den Implicit-Operator überladen würdest, solltest du deine Setter-Methode so belassen können, wie du es hast. Mal schnell aus dem Kopf heraus aufgeschrieben:
Delphi-Quellcode:
TBlub = class
private
  FRec : TMyRecord;
  procedure SetRec(Value: TMyRecord);
public
  property Rec : TMyRecord read FRec write SetRec;
end;

procedure TBlub.SetRec(Value: TMyRecord);
begin
  FRec := Value;
end;


TMyRecord = class
private
  FValue : Integer;
public
  operator Implicit(Value: Integer): TMyRecord;
  property Value : Integer read FVaule;
end;

TMyRecord.Implicit(Value: Integer): TMyRecord;
begin
  Result.FValue := Value;
end;

Iwo Asnet 23. Aug 2012 13:03

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179511)
Guter Code benötigt (fast) keine Doku.

Korrekt, aber das ist mit dem hier vorliegenden Problem (Zugriff über Metainformation, hier: Eigenschaftsnamen) nicht zu lösen.

Wir können das ganz einfach durch einen applikationsspezifischen Wrapper hinbekommen:

Delphi-Quellcode:
Type
  TSpecificConfig = Class
  Public
    Property MyInteger : Integer Read GetMyInteger  Write SetMyInteger;
  End;

...
Function TSpecificConfig.GetMyInteger : Integer;
Begin
  Result := VarToInt (Item['MyInteger']);
End;
Dies lässt sich noch vereinfachen, indem die Konfigurationsnamen in einem konstanten Array vorgehalten werden und es pro Datentyp einen IndexGetter und -setter gibt, also z.B. so:

Delphi-Quellcode:
Type
  TSpecificConfig = Class
    Const Values : Array [0..xx] of String = ('MyInt', 'MyString'...);
  Public
    Property MyInt : Integer index 0 Read GetInteger Write SetInteger;
    Property MyString : String index 1 Read GetString Write SetString;
  End;

...
Function TSpecificConfig.GeInteger (Index : Integer) : Integer;
Begin
  Result := VarToInt (Item[Values[Index]]);
End;
Der Zugriff erfolgt dann über eine Instanz der 'TSpecific'-Klasse.

Wie gesagt: Wer's toll findet, kann sich die Mühe machen.

Mir reicht:
Delphi-Quellcode:
Setup['MyInt'] := 4711;

-=ZGD=- 23. Aug 2012 14:56

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von bepe (Beitrag 1179512)
Zitat:

Zitat von -=ZGD=- (Beitrag 1179480)
Delphi-Quellcode:
type
  RConfig2 = record
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
  public
    property Key: String read fKey write fKey;
    property Value: Variant write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;

Ich würde im Record die Setter Methoden einfügen, nicht in der Config Klasse. Dann kannst du einfach
Delphi-Quellcode:
...AsInteger := 4711
tippen, anstatt ein Record übergeben zu müssen.

Zitat:

Zitat von -=ZGD=- (Beitrag 1179480)
Delphi-Quellcode:
type
  TConfig = class(TObject)
  private
    ...
    fList: TConfigList;
    fList2: TConfigList2;

    ...
    function GetVariantValue(aID: String): RConfig2;
    procedure SetVariantValue(aID: string; aValue: Variant);

    ...
  public
    constructor create;

    ...
    property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
    ...
  end;

Hier ist der Getter ausreichend. Aber wenn schon ein Setter da ist, dann definitiv mit RConfig2 arbeiten nicht mit Variant. Auch wenn ich nichts gegen Variant habe (doch eigentlich schon :-D), es macht hier einfach keinen Sinn.

Ich würde mich jetzt für die Variante mit Setter im Record entscheiden.
Die ist drin und sollte auch funktionieren...sollte.

Ich debugge.

Was passiert bei der jetzigen Konstellation:

Delphi-Quellcode:
type
  RConfig2 = record
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
    procedure SetAValue(aValue: Variant);
  public
    property Key: String read fKey write fKey;
    property Value: Variant read fValue write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;
und
Delphi-Quellcode:
property Item[aKey: String]: RConfig2 read GetVariantValue;
Ich strebe an, über
Code:
.Value:= ?
die Wertzuweisung zu machen.

Jedoch läuft er beim Aufruf von
Delphi-Quellcode:
myConfig.Item['dbconfig'].Value:= 2312
immer in
Delphi-Quellcode:
GetVariantValue
was mir auch logisch erscheint.
Nur wie bringe ich diesen Zugriff so hin, dass es nach meinem Wunsch funktioniert?

s.h.a.r.k 23. Aug 2012 15:01

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Du willst doch, dass es einfach funktioniert. Warum nimmst du dann nicht einfach das was ich geschrieben habe? Das was ich geschrieben habe, funktioniert nicht mal, da dann die Variable Key nicht mit übernommen wird. Shit...

-=ZGD=- 23. Aug 2012 15:11

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179534)
Du willst doch, dass es einfach funktioniert. Warum nimmst du dann nicht einfach das was ich geschrieben habe?

Dann muss ich wieder so viel umbauen... :shock:

Und ich muss so ehrlich sein und sagen, dass mir die Einarbeitungszeit in Implicit für diese Miniklasse als zu hoch erscheint.

Es sieht so aus, als ist meine Vorstellung in dieser Art auf meiner Codebasis doch nicht so ohne Weiteres umzusetzen...

Ich bekomme einfach diese
Code:
Inkompatible Typen
Warnung nicht weg.

Vor allem frage ich mich, wie es funktionieren soll, wenn der Setter rein im Record deklariert ist..

s.h.a.r.k 23. Aug 2012 15:25

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von -=ZGD=- (Beitrag 1179537)
Dann muss ich wieder so viel umbauen... :shock:

Viel wäre es ja nicht gewesen, sondern nur ein paar Zeilen. Nur leider hatte die Idee das gleiche Problem, wie du es im Moment hast.

Quick and Dirty könntest es so umbauen, dass du den folgenden Aufruf hast:
Delphi-Quellcode:
myConfig.Item['dbconfig'] := myConfig.Item['dbconfig'].SetValue(2312);
Schön ist aber was anders...

Würde dann eher vorschlagen, dass du der TConfig eine SetItemValue(Name: string; Value: Variant) Methode spendierst.

Oder eine ganz andere Alternative wäre es einen Zeiger auf den Record zurückzugeben, also bei GetVariantValue eien PRConfig2. Aber ob das so toll wäre...

-=ZGD=- 23. Aug 2012 15:30

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179539)
Zitat:

Zitat von -=ZGD=- (Beitrag 1179537)
Dann muss ich wieder so viel umbauen... :shock:

Viel wäre es ja nicht gewesen, sondern nur ein paar Zeilen. Nur leider hatte die Idee das gleiche Problem, wie du es im Moment hast.

Quick and Dirty könntest es so umbauen, dass du den folgenden Aufruf hast:
Delphi-Quellcode:
myConfig.Item['dbconfig'] := myConfig.Item['dbconfig'].SetValue(2312);
Schön ist aber was anders...

Würde dann eher vorschlagen, dass du der TConfig eine SetItemValue(Name: string; Value: Variant) Methode spendierst.

Oder eine ganz andere Alternative wäre es einen Zeiger auf den Record zurückzugeben, also bei GetVariantValue eien PRConfig2. Aber ob das so toll wäre...

Die Methode hatte ich drin, bzw. habe ich noch da nur leider lässt der Compiler es nicht durch.


Delphi-Quellcode:
procedure SetVariantValue(aID: String; aValue: Variant);
    ...
    procedure SetAValue(aValue: Variant);
  public
    ...
    property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
Der Record
Delphi-Quellcode:
type
  RConfig2 = record
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
    procedure SetAValue(aValue: Variant);
  public
    property Key: String read fKey write fKey;
    property Value: Variant read fValue write SetAValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;
Wo ist der Fehler? Ich bin nur noch am systematischen Probieren, aber ich komme auf keine Lösung - das nervt mich..

Hier scheint meiner Meinung nach das Problem zu sein, das
Delphi-Quellcode:
Item
vom Typ
Delphi-Quellcode:
RConfig2
ist?!

s.h.a.r.k 23. Aug 2012 15:38

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Folgender Vorschlag. Wirf diese Record-Geschichte über den Haufen und mach daraus eine Klasse. Dann klappt auch das mit dem Getter und du brauchst keinen Setter, da du ja nur Referenzen erhälst, wenn du via Item[Key] auf ein Config-Eintrag zugreifst. Für das Hinzufügen bzw. Löschen würde ich entsprechende Methoden in der TConfig-Klasse implementieren. Hier mal das, wie ich es mir gerade vorstelle:
Delphi-Quellcode:
type
  TConfigItem = class;
 
  TConfig = class(TObject)
  private
    FItems : TDictionary<string, TConfigItem>;
    function GetConfigItem(aID: String): TConfigValue;
  public
    constructor Create;
    procedure AddItem(AItem: TConfigItem);
    procedure RemoveItem(AItem: TConfigItem); overloads;
    procedure RemoveItem(const Key: string); overloads;
    function ItemExists(const Key: string): Boolean;
    property Item[aKey: String]: TConfigItem read GetConfigItem;
  end;


  TConfigItem = class
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
  public
    constructor Create(const AKey: string; Vaule: Variant);
    property Key: String read fKey;
    property Value: Variant write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;
PS: Natürlich musst du im Destruktor von TConfig die Items wieder freigeben. Oder du nimmst ein TObjectDictionary. Dann kümmert sich das Wörterbuch entsprechend darum, wenn beim OnCreate angibst, dass deine Werte Objekte sind.

Ich weiß nicht, ob du dich mit Interfaces auskennst, aber wenn du darauf setzen würdest, bräuchtest diese Freigabe-Geschichte nicht weiter zu beachten,.

-=ZGD=- 23. Aug 2012 15:41

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179542)
Folgender Vorschlag. Wirf diese Record-Geschichte über den Haufen und mach daraus eine Klasse. Dann klappt auch das mit dem Getter und du brauchst keinen Setter, da du ja nur Referenzen erhälst, wenn du via Item[Key] auf ein Config-Eintrag zugreifst. Für das Hinzufügen bzw. Löschen würde ich entsprechende Methoden in der TConfig-Klasse implementieren. Hier mal das, wie ich es mir gerade vorstelle:
Delphi-Quellcode:
type
  TConfigItem = class;
 
  TConfig = class(TObject)
  private
    FItems : TDictionary<string, TConfigItem>;
    function GetConfigItem(aID: String): TConfigValue;
  public
    constructor Create;
    procedure AddItem(AItem: TConfigItem);
    procedure RemoveItem(AItem: TConfigItem); overloads;
    procedure RemoveItem(const Key: string); overloads;
    function ItemExists(const Key: string): Boolean;
    property Item[aKey: String]: TConfigItem read GetConfigItem;
  end;


  TConfigItem = class
  private
    fKey: String;
    fValue: Variant;
    function GetAsString: String;
    function GetAsInteger: Integer;
  public
    constructor Create(const AKey: string; Vaule: Variant);
    property Key: String read fKey;
    property Value: Variant write fValue;
    property AsString: String read GetAsString;
    property AsInteger: Integer read GetAsInteger;
  end;
PS: Natürlich musst du im Destruktor von TConfig die Items wieder freigeben. Oder du nimmst ein TObjectDictionary. Dann kümmert sich das Wörterbuch entsprechend darum, wenn beim OnCreate angibst, dass deine Werte Objekte sind.

Ich weiß nicht, ob du dich mit Interfaces auskennst, aber wenn du darauf setzen würdest, bräuchtest diese Freigabe-Geschichte nicht weiter zu beachten,.

´

Ich werde mal versuchen, das bei Gelegenheit umzusetzen.
Ich hätte nur gedacht, dass diese "Idee" recht trivial ist.

Interfaces, Generics, Dictionaries sind Neuheiten für mich, für die ich (leider!) noch keine Zeit hatte...

Danke, dass ihr euch die Zeit nehmt/genommen habt.

bepe 23. Aug 2012 18:43

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
1. Habe ich ein wenig den Überblick darüber verloren, wo du gerade was ausprobiert hast und wo der Fehler steckt.
2. Aus Zeitmangel, Überhitzung oder einfach weil ich mittlerweile doch Ü30 bin, habe ich dich mit dem Record auf eine falsche Spur geschickt. Hab es gerade erfolglos ausprobiert und habe selber gerade keine Ahnung was ich falsch mache.
3. Mach aus dem Record eine Klasse und alles funktioniert, wenn dein bisheriger Versuch ungefähr wie meiner aussieht:

Delphi-Quellcode:
  TConfigItem = class
  strict private
    FWert: String;

    function GetAsInteger: Integer;
    function GetAsVariant: Variant;

    procedure SetAsInteger(Value: Integer);
    procedure SetAsVariant(Value: Variant);

  public
    property AsInteger: Integer read GetAsInteger write SetAsInteger;
    property Value: Variant read GetAsVariant write SetAsVariant;

  end;

  TConfigItemDic = class(TDictionary<String, TConfigItem>);

  TConfigKlasse = class
  strict private
    FItems: TConfigItemDic;

  strict protected
    function GetConfigItem(Index: String): TConfigItem;

  public
    constructor Create;
    destructor Destroy; override;

    property Item[Index: String]: TConfigItem read GetConfigItem;

  end;


{ TConfigItem }

function TConfigItem.GetAsInteger: Integer;
begin
  Result := StrToInt(FWert);
end;

function TConfigItem.GetAsVariant: Variant;
begin
  Result := FWert;
end;

procedure TConfigItem.SetAsInteger(Value: Integer);
begin
  FWert := IntToStr(Value);
end;

procedure TConfigItem.SetAsVariant(Value: Variant);
begin
  FWert := Value;
end;

{ TConfigKlasse }

constructor TConfigKlasse.Create;
var
  tmpTest: TConfigItem;
begin
  FItems := TConfigItemDic.Create;

// Nur zum testen...
  tmpTest := TConfigItem.Create;
  tmpTest.AsInteger := 4711;
  FItems.Add('Test', tmpTest);
end;

destructor TConfigKlasse.Destroy;
begin
  FItems.Free;
  inherited;
end;

function TConfigKlasse.GetConfigItem(Index: String): TConfigItem;
begin
  Result := FItems[Index];
end;


//Der Test
procedure TForm1.FormCreate(Sender: TObject);
var
  tmpConfig: TConfigKlasse;
begin
  tmpConfig := TConfigKlasse.Create;
  try
    tmpConfig.Item['Test'].AsInteger := 42;
    Caption := tmpConfig.Item['Test'].Value;
  finally
    tmpConfig.Free;
  end;
end;

-=ZGD=- 24. Aug 2012 09:46

AW: Property via AsString;AsInteger;AsBoolean;.. laden
 
Hi bepe,

absolut kein Problem.
Ich bin und war für jede Hilfe dankbar.

Ich find´s sehr toll, dass du eine Beispielklasse implementiert hast.
Die hab ich mir gleich ausgedruckt und werde mich am Wochenende damit beschäftigen - dann kann ich Dictionarys abhaken :-)

Ich danke euch.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:09 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz