![]() |
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:
in dem einfach der Schlüssel übergeben wird funktioniert problemlos.
property Items[aKey: String]: String read GetValue write SetValue;
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:
genügen würde und ich den Port als
myConfig.Items['dbport'].AsInteger
Delphi-Quellcode:
erhalten?
Integer
Über Links und Hilfestellung jeder Art bin ich dankbar. Auch wenn wie ![]() Ich danke vielmals. |
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:
auf die Methoden und Propertys zugreifen. Und du müsstest nur noch pro Datentyp eine Property mit entsprechender Getter Methode bereitstellen.
myConfig.Items['dbport'].WasAuchImmer
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. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Zitat:
Zitat:
Ich danke dir - einstweilen.. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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:
anbieten.
TDictionary
|
AW: Property via AsString;AsInteger;AsBoolean;.. laden
So sieht es jetzt bei mir aus
Ich habe ein
Delphi-Quellcode:
über alle Einträge:
record
Delphi-Quellcode:
Daraus einen eigenen Typ, als dynamisches Array
type
RConfig = record Key: String; Value: String; end;
Delphi-Quellcode:
Welches Verwendung in der Klassendeklaration
type
TConfigList = array of RConfig;
Delphi-Quellcode:
in der privaten Variable
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;
Delphi-Quellcode:
Anwendung findet.
fList
Wie würde jetzt meine Deklaration aussehen, wenn ich die
Delphi-Quellcode:
als Objekt handhaben möchte.
Items
Meine Idee wäre dann
Delphi-Quellcode:
Woraus sich ergeben würde, dass
type
TConfigItem = class(TObject) private function GetAsInteger: Integer; function GetAsString: String; public property AsInteger: Integer read GetAsInteger; property AsString: String read GetAsString; end;
Delphi-Quellcode:
wird.
Items[aKey: String]: TConfigItem
Allerdings mit welchem
Delphi-Quellcode:
?
read
|
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
änderst du auch nur den Typ und greifst natürlich auf dein
read
Delphi-Quellcode:
array zu.
TConfigList
*Wenn du das Objekt entfernst und nur mit dem Record arbeitest, sehe Items so aus
Delphi-Quellcode:
Items[aKey: String]: RConfig
|
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:
Vielleicht findest die Idee ja interessant :)
// 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; |
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; |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Danke für dein Hilfe bepe.
Auf Grundlage deiner Informationen bin ich jetzt soweit vorangeschritten:
Delphi-Quellcode:
Die Implementierung der GET/SET
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;
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:
sieht nun bei mir so aus (ja, noch keine Hashtable)
GetVariantValue
Delphi-Quellcode:
Ich dachte mir, ich nehme
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;
Delphi-Quellcode:
als Typ für die Value, denn somit bin ich da flexibel...eher ungut?
Variant
Woran ich jetzt allerdings scheitere ist die Implementierung von
Delphi-Quellcode:
.
SetVariantValue
Der Compiler sagt
Code:
.
Inkompatible Typen
Delphi-Quellcode:
Den Versuch, einfach ein
procedure Tconfig.SetVariantValue(aID: string; aValue: Variant);
begin end;
Code:
zu übergeben ist ebenfalls gescheitert.
RConfig2
Wo seh ich den Wald vor lauter Bäumen nicht? Ist die Implementierung in den Grundzügen so korrekt? |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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:
leer ist, beschmeißt mich Delphi natürlich mit der korrekten Fehlermeldung
Value
Code:
. Das würde ich umgehen wollen und einfach von vorn herein
"" ist kein gültiger Integer-Wert
Code:
übergeben.
0
|
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. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
@-=ZGD=-:
Gut, dann müßtest Du natürlich vorher den Inhalt des Variant-Wertes testen:
Delphi-Quellcode:
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.
if VarIsType(Items['xxx'],varInteger)
then IntegerWert:=Items['xxx'] else IntegerWert:=IntegerDefault; Zitat:
|
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:
|
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... |
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. |
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:
und ich dann schon fertig meine Konvertierung, whatever gemacht habe.
myConfig.Items['iwas'].AsBoolen
Der Hintergrund ist total simpel: Ich habe eine SQLite-3-Datenbank in welcher eine Tabelle
Code:
ist mit den Feldern
config
Code:
.
key, value
Code:
kann jeden beliebigen Wert annehmen.
value
Steht da jetzt -1 drin, kann ich genauso mit
Code:
, als auch mit
.AsInteger
Code:
Abfragen gestalten.
.AsBoolean
Das ist einfach n Stück weit flexibel, ohne dass ich andere Entwickler mit strikten Typen bewerfen muss.
Code:
sind einfach vom Query abgekupfert.
.AsInteger, .AsString, .AsBoolean, ...
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 ;-) |
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. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Jetzt nicht den Kopf zerbrechen, warum und woher.. :) Pass auf:
Code:
Das wäre jetzt ein Auszug aus der Config-Tabelle.
KEY | VALUE
dbhost | localhost dbport | 3306 dbuser | root ... Bisher wurde alles als String ausgelesen. Nun kann ich meiner Port-Komponente keinen String zuweisen. Daher die Intention
Code:
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.
.AsInteger
Delphi-Quellcode:
SetVariantValue ist ein Prozedur
property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
Delphi-Quellcode:
.
procedure SetVariantValue(aID: String; aValue: Variant);
Ich hab auch schon versucht,
Code:
zu übergeben, aber ich scheitere...
RConfig2
|
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... |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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 |
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:
Wir hatten bisher nie die Muße, über den Sinn zu diskutieren. Die Unit wurde in 30 min zusammengebaut und funktioniert.
ValueAsVariant := Setup['MySetting'];
ValueAsInteger := Setup.AsInteger['MyIntegerSetting']; ValueAsBoolean := Setup.AsBoolean['MyBooleanSetting']; ... 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. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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? |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Zitat:
Folgendes Beispiel:
Delphi-Quellcode:
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:
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; Zitat:
|
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Delphi-Quellcode:
tippen, anstatt ein Record übergeben zu müssen.
...AsInteger := 4711
Zitat:
|
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; |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Wir können das ganz einfach durch einen applikationsspezifischen Wrapper hinbekommen:
Delphi-Quellcode:
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:
Type
TSpecificConfig = Class Public Property MyInteger : Integer Read GetMyInteger Write SetMyInteger; End; ... Function TSpecificConfig.GetMyInteger : Integer; Begin Result := VarToInt (Item['MyInteger']); End;
Delphi-Quellcode:
Der Zugriff erfolgt dann über eine Instanz der 'TSpecific'-Klasse.
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; Wie gesagt: Wer's toll findet, kann sich die Mühe machen. Mir reicht:
Delphi-Quellcode:
Setup['MyInt'] := 4711;
|
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Die ist drin und sollte auch funktionieren...sollte. Ich debugge. Was passiert bei der jetzigen Konstellation:
Delphi-Quellcode:
und
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;
Delphi-Quellcode:
Ich strebe an, über
property Item[aKey: String]: RConfig2 read GetVariantValue;
Code:
die Wertzuweisung zu machen.
.Value:= ?
Jedoch läuft er beim Aufruf von
Delphi-Quellcode:
immer in
myConfig.Item['dbconfig'].Value:= 2312
Delphi-Quellcode:
was mir auch logisch erscheint.
GetVariantValue
Nur wie bringe ich diesen Zugriff so hin, dass es nach meinem Wunsch funktioniert? |
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...
|
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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:
Warnung nicht weg.
Inkompatible Typen
Vor allem frage ich mich, wie es funktionieren soll, wenn der Setter rein im Record deklariert ist.. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Quick and Dirty könntest es so umbauen, dass du den folgenden Aufruf hast:
Delphi-Quellcode:
Schön ist aber was anders...
myConfig.Item['dbconfig'] := myConfig.Item['dbconfig'].SetValue(2312);
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... |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
Delphi-Quellcode:
Der Record
procedure SetVariantValue(aID: String; aValue: Variant);
... procedure SetAValue(aValue: Variant); public ... property Item[aKey: String]: RConfig2 read GetVariantValue write SetVariantValue;
Delphi-Quellcode:
Wo ist der Fehler? Ich bin nur noch am systematischen Probieren, aber ich komme auf keine Lösung - das nervt mich..
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; Hier scheint meiner Meinung nach das Problem zu sein, das
Delphi-Quellcode:
vom Typ
Item
Delphi-Quellcode:
ist?!
RConfig2
|
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:
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.
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; 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,. |
AW: Property via AsString;AsInteger;AsBoolean;.. laden
Zitat:
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. |
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; |
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 08:51 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 by Thomas Breitkreuz