Registriert seit: 27. Nov 2017
2.598 Beiträge
Delphi 7 Professional
|
AW: KI Code : Editor für Daten
31. Jul 2025, 09:23
Diese function ParseDataType(const DataTypeStr: string): TInputType;
gibt es mehrfach. Warum?
FDataItemList.LoadFromStringList(StringList);
wird mehrfach hintereinander aufgerufen, warum?
Warum gibt es leere Methoden?
Wird wirklich alles, was im Uses steht benötigt?
Habe mal Perplexity.ai um Überarbeitung gebeten. Dashier ist dabei herausgekommen:

Zitat von Frage:
Lässt sich nachfolgende Unit vereinfachen? Enthält sie Redundanzen o. ä.?
Delphi-Quellcode:
unit StudyDatamodule;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants, Rtti, TypInfo, Math,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Memo.Types,
FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo,
Unit_Tdatamodule, FMX.Edit;
type
TForm1 = class(TForm)
mmo1: TMemo;
btn_InitLIst: TButton;
edt_propertyValue: TEdit;
lbl_PropertyName: TLabel;
btn_Up: TButton;
btn_Down: TButton;
edt_Datatype: TEdit;
btn_ListasJSON: TButton;
procedure FormCreate(Sender: TObject);
procedure btn_UpClick(Sender: TObject);
procedure btn_DownClick(Sender: TObject);
procedure edt_propertyValueEnter(Sender: TObject);
procedure edt_propertyValueExit(Sender: TObject);
procedure edt_propertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
procedure edt_propertyValueKeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
procedure btn_ListasJSONClick(Sender: TObject);
private
{ Private declarations }
FDataItemList: TDataItemList;
FItemIndex: Integer;
procedure DisplayActiveItem( index: Integer);
procedure SaveInputToItem;
function ValidateInput( const Value: string; DataType: TInputType): Boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
// Example Initialization from StringList
// ere’s an updated FormCreate method that uses the new functionality:
procedure TForm1.btn_DownClick(Sender: TObject);
begin
FItemIndex := FItemIndex - 1;
FItemIndex := Max(FItemIndex, 0);
DisplayActiveItem(FItemIndex);
end;
procedure TForm1.btn_ListasJSONClick(Sender: TObject);
begin
mmo1.Lines.Add(FDataItemList.AsJSON);
end;
procedure TForm1.btn_UpClick(Sender: TObject);
begin
FItemIndex := FItemIndex + 1;
FItemIndex := Min(FItemIndex, FDataItemList.Count - 1);
DisplayActiveItem(FItemIndex);
end;
procedure TForm1.DisplayActiveItem( index: Integer);
var
DataItem: TDataItem;
begin
DataItem := FDataItemList.Items[ Index];
lbl_PropertyName.Text := DataItem. Name;
edt_propertyValue.Text := DataItem.Value;
edt_Datatype.Text := GetEnumName(TypeInfo(TInputType),
Integer(DataItem.DataType));
end;
procedure TForm1.edt_propertyValueEnter(Sender: TObject);
begin
/// check for valid user inputs and save data
end;
procedure TForm1.edt_propertyValueExit(Sender: TObject);
begin
SaveInputToItem;
end;
// Restrict input while typing
procedure TForm1.edt_propertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
var
DataItem: TDataItem;
begin
if (FItemIndex < 0) or (FItemIndex >= FDataItemList.Count) then
Exit;
DataItem := FDataItemList.Items[FItemIndex];
// Restrict input for numbers and booleans
case DataItem.DataType of
itNumber:
if not(KeyChar in [' 0' .. ' 9', ' .', ' -', #8]) then
// Allow digits, decimal point, negative sign, and backspace
KeyChar := #0;
itBoolean:
if not(KeyChar in [' t', ' r', ' u', ' e', ' f', ' a', ' l', ' s', #8]) then
// Allow 'true' and 'false'
KeyChar := #0;
end;
end;
procedure TForm1.edt_propertyValueKeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
begin
end;
// Save the user's input into the active data item
procedure TForm1.SaveInputToItem;
var
DataItem: TDataItem;
UserInput: string;
begin
if (FItemIndex < 0) or (FItemIndex >= FDataItemList.Count) then
Exit;
DataItem := FDataItemList.Items[FItemIndex];
UserInput := Trim(edt_propertyValue.Text);
// Validate the input
if not ValidateInput(UserInput, DataItem.DataType) then
begin
ShowMessage(Format(' Invalid input for %s. Expected a %s value.',
[DataItem. Name, GetEnumName(TypeInfo(TInputType),
Integer(DataItem.DataType))]));
edt_propertyValue.SetFocus; // Return focus to the input field
Exit;
end;
// Save the validated input to the data item's Value field
DataItem.Value := UserInput;
end;
// Validate user input based on the data type of the current item
function TForm1.ValidateInput( const Value: string;
DataType: TInputType): Boolean;
var
TempFloat: Double;
TempBoolean: Boolean;
begin
Result := False;
case DataType of
itString:
Result := True; // Any string is valid
itNumber:
Result := TryStrToFloat(Value, TempFloat); // Validate numeric input
itBoolean:
Result := TryStrToBool(Value, TempBoolean);
// Validate boolean input ('true' or 'false')
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
StringList: TStringList;
DataItem: TDataItem;
begin
StringList := TStringList.Create;
FDataItemList := TDataItemList.Create(True); // Owns objects
try
// Populate TStringList with data using '|' as the delimiter
StringList.Add(' EnableLogging|Boolean|true');
StringList.Add(' MaxLogFileSize|Number|1048576');
// File size in bytes (1 MB)
StringList.Add(' LogFilePath|String|C:\Logs\app.log');
StringList.Add(' EnableAutoSave|Boolean|true');
StringList.Add(' AutoSaveInterval|Number|10'); // Interval in minutes
StringList.Add(' Theme|String|dark');
StringList.Add(' ShowNotifications|Boolean|true');
StringList.Add(' MaxRetries|Number|3');
StringList.Add(' RetryDelay|Number|5'); // Delay in seconds
StringList.Add(' EnableEncryption|Boolean|true');
StringList.Add(' EncryptionKey|String|default_key');
StringList.Add(' AllowGuestAccess|Boolean|false');
StringList.Add(' DefaultLanguage|String|en');
StringList.Add(' TimeoutDuration|Number|30'); // Timeout in seconds
StringList.Add(' EnableDebugMode|Boolean|false');
FDataItemList.LoadFromStringList(StringList);
// Load data into TDataItemList
FDataItemList.LoadFromStringList(StringList);
// Display loaded items for verification
for DataItem in FDataItemList do
mmo1.Lines.Add(Format(' Name: %s, DataType: %s, DefaultValue: %s',
[DataItem. Name, GetEnumName(TypeInfo(TInputType),
Integer(DataItem.DataType)), DataItem.DefaultValue]));
DisplayActiveItem(FItemIndex);
finally
StringList.Free;
end;
end;
end.

Zitat von Antwort:
Gerne! Ich habe Ihre Delphi- Unit durchgesehen und nach folgenden Kriterien beurteilt:
- "Redundanzen/Doppelungen"
- "Codevereinfachung" – sowohl stilistisch als auch logisch
- "Allgemeine "Best Practice"-Hinweise"
## Redundanzen & Vereinfachung
1. "Doppelte Aufrufe"
Delphi-Quellcode:
FDataItemList.LoadFromStringList(StringList);
// Load data into TDataItemList
FDataItemList.LoadFromStringList(StringList);

Zitat von Fortsetzung der Antwort:
? "Hier wird 'LoadFromStringList' zweimal direkt hintereinander aufgerufen. Das ist redundant, bitte einen Aufruf entfernen."
2. "Imports / Uses-Klausel"
- Prüfen Sie, ob wirklich alle Einträge benötigt werden (z.B. 'System.Types', 'System.UITypes', 'System.Variants' sowie ggf. aus FMX-Bibliotheken).
3. "Leere Methoden"
Delphi-Quellcode:
procedure TForm1.edt_propertyValueEnter(Sender: TObject);
begin
/// check for valid user inputs and save data
end;
procedure TForm1.edt_propertyValueKeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
begin
end;

Zitat von Fortsetzung der Antwort:
? "Kann vermutlich ganz entfernt werden, wenn kein Code folgt."
4. "Index-Handling kann zusammengefasst/gesichert werden"
Sie haben in Up/Down-Buttons dieses Muster:
Delphi-Quellcode:
FItemIndex := FItemIndex - 1;
FItemIndex := Max(FItemIndex, 0);

Zitat von Fortsetzung der Antwort:
- und analog für +1 und Min(...)
"Tipp:"
Sie könnten eine Hilfsfunktion wie 'SetItemIndex' einführen, die sowohl den Bereich prüft als auch gleich das Item anzeigt. Dann entfällt in beiden Button-Handlern der gleichartige Code.
Delphi-Quellcode:
procedure TForm1.SetItemIndex(AIndex: Integer);
begin
if FDataItemList.Count = 0 then Exit;
FItemIndex := EnsureRange(AIndex, 0, FDataItemList.Count-1);
DisplayActiveItem(FItemIndex);
end;

Zitat von Fortsetzung der Antwort:
In Down/Up nur noch:
Delphi-Quellcode:
SetItemIndex(FItemIndex-1);
SetItemIndex(FItemIndex+1);

Zitat von Fortsetzung der Antwort:
Delphi kennt 'EnsureRange' ab Berlin, sonst mit 'Min/Max' selbst bauen.
5. "Schreibweise & Konsistenz"
- "Namenskonventionen": Einheitliche Namen verwenden (z.B. 'btn_InitLIst' ? lieber überall CamelCase oder Underscore).
- "Kommentare aktualisieren": Entfernen Sie Altlasten ("// Example Initialization from StringList", da direkt darunter der echte Code steht).
- "Typangaben konsistent": Die Angabe von 'TDataItemList' usw. sollte für Dritte klar sein (z.B. woher diese Typen stammen).
6. "Vereinfachungen am Beispiel"
z.B. in DisplayActiveItem:
Delphi-Quellcode:
procedure TForm1.DisplayActiveItem(index: Integer);
begin
if (index = FDataItemList.Count) then Exit;
with FDataItemList.Items[index] do
begin
lbl_PropertyName.Text := Name;
edt_propertyValue.Text := Value;
edt_Datatype.Text := GetEnumName(TypeInfo(TInputType), Integer(DataType));
end;
end;

Zitat von Fortsetzung der Antwort:
- Fehler abfangen (out-of-bounds)
- 'with' ist in kleinen Blöcken erlaubt und erhöht hier die Lesbarkeit etwas
7. "Optional: Initialwert 'FItemIndex' sichern"
Wird in 'FormCreate' 'FItemIndex' zu Beginn irgendwo gesetzt? Wenn nicht, ist er evtl. undefiniert (kann zu Problemen führen).
Delphi-Quellcode:
FItemIndex := 0;
if FDataItemList.Count > 0 then
DisplayActiveItem(FItemIndex);

Zitat von Fortsetzung der Antwort:
8. "Kleinere Stil-Verbesserungen"
- In 'SaveInputToItem' könnten Sie 'Trim(edt_propertyValue.Text)' schon vor der Validierung übernehmen.
- In der Validierung: Case Branches ggfls. einrücken.
## Zusammenfassung
"Redundanzen:"
- 'LoadFromStringList' wird doppelt aufgerufen.
- Leere Methoden vorhanden.
- Up/Down Navigation ähnlich gelöst, kann ausgelagert werden.
"Vereinfachungsvorschläge:"
- Index-Handling über Methode vereinheitlichen.
- Methode zur Anzeige des aktiven Items könnte abgesichert und kompakter geschrieben werden.
- Variablennamen und Kommentare aufräumen.
"> Die Struktur ist sonst bereits modular und übersichtlich.
> Die Logik ist klar, nur etwas "Boilerplate" und Redundanzen, die sich leicht beheben lassen."
Wenn Sie konkrete Vereinfachung einstellen möchten, kann ich Ihnen den Codeblock entsprechend "aufräumen" und alternative Architektur vorschlagen.
Soll ich das tun?

Zitat von Frage:
Könnten Sie mir bitte eine nach Ihren Kriterien überarbeitete Unit zur Verfügung stellen? Um die Uses-Klause werde ich mich dann selbst kümmern.

Zitat von Antwort:
Sehr gerne! Hier ist eine überarbeitete Version Ihrer Unit, in der alle angesprochenen Optimierungen umgesetzt wurden:
- Redundante Aufrufe entfernt
- Leere Methoden gelöscht
- Index-Korrektur und Anzeige vereinheitlicht
- Naming vereinheitlicht
- Diverse Stil- und Fehlerbehandlungs-Verbesserungen
- Kommentare (sofern nötig) erneuert
"Hinweise:"
- Die Typen 'TDataItemList', 'TDataItem' und 'TInputType' stammen vermutlich aus Ihrer eigenen Unit – hier bleibe ich bei der Annahme wie gehabt.
- Die uses-Klausel wurde auf „minimal“ reduziert (Sie ergänzen sie selbst).
Delphi-Quellcode:
unit StudyDatamodule;
interface
uses
System.SysUtils, System.Classes, Math, FMX.Types, FMX.Controls, FMX.Forms;
type
TForm1 = class(TForm)
mmo1: TMemo;
btnInitList: TButton;
edtPropertyValue: TEdit;
lblPropertyName: TLabel;
btnUp: TButton;
btnDown: TButton;
edtDatatype: TEdit;
btnListAsJSON: TButton;
procedure FormCreate(Sender: TObject);
procedure btnUpClick(Sender: TObject);
procedure btnDownClick(Sender: TObject);
procedure edtPropertyValueExit(Sender: TObject);
procedure edtPropertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
procedure btnListAsJSONClick(Sender: TObject);
private
FDataItemList: TDataItemList;
FItemIndex: Integer;
procedure SetItemIndex(AIndex: Integer);
procedure DisplayActiveItem( Index: Integer);
procedure SaveInputToItem;
function ValidateInput( const Value: string; DataType: TInputType): Boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R '.fmx}
procedure TForm1.FormCreate(Sender: TObject);
var
StringList: TStringList;
DataItem: TDataItem;
begin
StringList := TStringList.Create;
FDataItemList := TDataItemList.Create(True);
try
// Populate with demo data (Delimiter: '|')
StringList.Add(' EnableLogging|Boolean|true');
StringList.Add(' MaxLogFileSize|Number|1048576');
StringList.Add(' LogFilePath|String|C:\Logs\app.log');
StringList.Add(' EnableAutoSave|Boolean|true');
StringList.Add(' AutoSaveInterval|Number|10');
StringList.Add(' Theme|String|dark');
StringList.Add(' ShowNotifications|Boolean|true');
StringList.Add(' MaxRetries|Number|3');
StringList.Add(' RetryDelay|Number|5');
StringList.Add(' EnableEncryption|Boolean|true');
StringList.Add(' EncryptionKey|String|default_key');
StringList.Add(' AllowGuestAccess|Boolean|false');
StringList.Add(' DefaultLanguage|String|en');
StringList.Add(' TimeoutDuration|Number|30');
StringList.Add(' EnableDebugMode|Boolean|false');
FDataItemList.LoadFromStringList(StringList);
// Anzeige fürs Debugging
for DataItem in FDataItemList do
mmo1.Lines.Add(Format(' Name: %s, DataType: %s, DefaultValue: %s',
[DataItem. Name, GetEnumName(TypeInfo(TInputType),
Integer(DataItem.DataType)), DataItem.DefaultValue]));
FItemIndex := 0;
if FDataItemList.Count > 0 then
DisplayActiveItem(FItemIndex);
finally
StringList.Free;
end;
end;
// Unified Item-Index Steuerung (mit Bereichsprüfung)
procedure TForm1.SetItemIndex(AIndex: Integer);
begin
if FDataItemList.Count = 0 then Exit;
FItemIndex := Math.EnsureRange(AIndex, 0, FDataItemList.Count - 1);
DisplayActiveItem(FItemIndex);
end;
// Buttons für Hoch/Runter
procedure TForm1.btnUpClick(Sender: TObject);
begin
SetItemIndex(FItemIndex + 1);
end;
procedure TForm1.btnDownClick(Sender: TObject);
begin
SetItemIndex(FItemIndex - 1);
end;
procedure TForm1.DisplayActiveItem( Index: Integer);
var
DataItem: TDataItem;
begin
if ( Index = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[ Index];
lblPropertyName.Text := DataItem. Name;
edtPropertyValue.Text := DataItem.Value;
edtDatatype.Text := GetEnumName(TypeInfo(TInputType), Integer(DataItem.DataType));
end;
// Eingabe validieren und speichern beim Verlassen des Felds
procedure TForm1.edtPropertyValueExit(Sender: TObject);
begin
SaveInputToItem;
end;
// Eingaben während der Eingabe einschränken (für Zahlen und Boolean)
procedure TForm1.edtPropertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
var
DataItem: TDataItem;
begin
if (FItemIndex = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[FItemIndex];
case DataItem.DataType of
itNumber:
if not (KeyChar in [' 0'..' 9', ' .', ' -', #8]) then KeyChar := #0;
itBoolean:
if not (KeyChar in [' t',' r',' u',' e',' f',' a',' l',' s',#8]) then KeyChar := #0;
end;
end;
// Speichern des Benutzereingabe in das gerade aktive DataItem
procedure TForm1.SaveInputToItem;
var
DataItem: TDataItem;
UserInput: string;
begin
if (FItemIndex = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[FItemIndex];
UserInput := Trim(edtPropertyValue.Text);
if not ValidateInput(UserInput, DataItem.DataType) then
begin
ShowMessage(Format(' Invalid input for %s. Expected a %s value.',
[DataItem. Name, GetEnumName(TypeInfo(TInputType), Integer(DataItem.DataType))]));
edtPropertyValue.SetFocus;
Exit;
end;
DataItem.Value := UserInput;
end;
// Inputvalidierung nach Datentyp
function TForm1.ValidateInput( const Value: string; DataType: TInputType): Boolean;
var
TempFloat: Double;
TempBoolean: Boolean;
begin
case DataType of
itString:
Result := True;
itNumber:
Result := TryStrToFloat(Value, TempFloat);
itBoolean:
Result := TryStrToBool(Value, TempBoolean);
else
Result := False;
end;
end;
// JSON-Export
procedure TForm1.btnListAsJSONClick(Sender: TObject);
begin
mmo1.Lines.Add(FDataItemList.AsJSON);
end;
end.

Zitat von Fortsetzung der Antwort:
"Ergänzende Hinweise:"
- Die leeren Methoden, nicht verwendeten Prozeduren und doppelten Aufrufe wurden entfernt.
- Die Item-Index-Kontrolle ist jetzt gekapselt.
- Namen und Groß-/Kleinschreibung der Controls und Variablen wurden „eng an Delphi-Konventionen“ angepasst.
- Alle Fehlerfälle werden geprüft; programmlogisch ist alles enger gekapselt.
- Die uses-Klausel müssen Sie nach Ihrer FMX- und eigenen Typ-Definition ergänzen.
Falls Sie spezielle Anforderungen (z.B. weitere Fehlerbehandlung, evtl. Undo/Redo, komplexere Validierung oder Designs) benötigen: Bitte Bescheid geben!

Zitat von Frage:
Lässt sich nachfolgende Unit vereinfachen? Enthält sie Redundanzen o. ä.?
Delphi-Quellcode:
unit Unit_TDataModule;
interface
uses
System.Classes, System.SysUtils, System.Generics.Collections, System.JSON,
System.IOUtils, TypInfo;
type
// Enum to represent input types
TInputType = (itString, itNumber, itBoolean);
// Class representing individual data items
TDataItem = class
private
FName: string;
FDataType: TInputType;
FValue: string;
FDefaultValue: string;
public
property Name: string read FName write FName;
property DataType: TInputType read FDataType write FDataType;
property Value: string read FValue write FValue;
property DefaultValue: string read FDefaultValue write FDefaultValue;
constructor Create( const AName: string; ADataType: TInputType; const ADefaultValue: string); overload;
procedure ResetToDefault; // Resets the Value to DefaultValue
end;
// Class representing a list of data items
TDataItemList = class(TObjectList<TDataItem>)
private
function GetAsJSON: string; // Returns JSON representation of the list
public
property AsJSON: string read GetAsJSON; // Read-only property to get JSON string
procedure LoadFromStringList(AStringList: TStringList); // Load items from a string list
procedure SaveToFile( const AFileName: string); // Save items to a JSON file
procedure LoadFromFile( const AFileName: string); // Load items from a JSON file
end;
implementation
{ TDataItem }
constructor TDataItem.Create( const AName: string; ADataType: TInputType; const ADefaultValue: string);
begin
inherited Create;
FName := AName;
FDataType := ADataType;
FDefaultValue := ADefaultValue;
FValue := ' '; // Initialize Value to unset (empty string)
end;
procedure TDataItem.ResetToDefault;
begin
FValue := FDefaultValue; // Reset Value to the DefaultValue
end;
{ TDataItemList }
function TDataItemList.GetAsJSON: string;
var
JSONArray: TJSONArray;
JSONObject: TJSONObject;
DataItem: TDataItem;
begin
JSONArray := TJSONArray.Create;
try
// Iterate through the items and convert them to JSON
for DataItem in Self do
begin
JSONObject := TJSONObject.Create;
JSONObject.AddPair(' Name', DataItem. Name);
JSONObject.AddPair(' DataType', GetEnumName(TypeInfo(TInputType), Integer(DataItem.DataType))); // Convert enum to string
JSONObject.AddPair(' Value', DataItem.Value);
JSONObject.AddPair(' DefaultValue', DataItem.DefaultValue);
JSONArray.AddElement(JSONObject);
end;
Result := JSONArray.ToJSON; // Return the JSON string
finally
JSONArray.Free;
end;
end;
procedure TDataItemList.LoadFromStringList(AStringList: TStringList);
var
i: Integer;
LineParts: TArray< string>;
Name, DefaultValue: string;
DataType: TInputType;
function ParseDataType( const DataTypeStr: string): TInputType;
begin
if SameText(DataTypeStr, ' Boolean') then
Result := itBoolean
else if SameText(DataTypeStr, ' Number') then
Result := itNumber
else if SameText(DataTypeStr, ' String') then
Result := itString
else
raise Exception.CreateFmt(' Unknown data type: %s', [DataTypeStr]);
end;
begin
for i := 0 to AStringList.Count - 1 do
begin
LineParts := AStringList[i].Split([' |']);
if Length(LineParts) <> 3 then
raise Exception.CreateFmt(' Invalid format in line %d: %s', [i + 1, AStringList[i]]);
Name := LineParts[0];
DataType := ParseDataType(LineParts[1]);
DefaultValue := LineParts[2];
// Add the item and set both DefaultValue and Value
Self.Add(TDataItem.Create( Name, DataType, DefaultValue));
Self.Last.Value := DefaultValue; // Initialize Value to DefaultValue
end;
end;
procedure TDataItemList.SaveToFile( const AFileName: string);
begin
// Save the JSON representation to a file
TFile.WriteAllText(AFileName, AsJSON);
end;
procedure TDataItemList.LoadFromFile( const AFileName: string);
var
JSONArray: TJSONArray;
JSONObject: TJSONObject;
JSONString: string;
i: Integer;
Name, DataTypeStr, Value, DefaultValue: string;
DataType: TInputType;
function ParseDataType( const DataTypeStr: string): TInputType;
begin
if SameText(DataTypeStr, ' itBoolean') then
Result := itBoolean
else if SameText(DataTypeStr, ' itNumber') then
Result := itNumber
else if SameText(DataTypeStr, ' itString') then
Result := itString
else
raise Exception.CreateFmt(' Unknown data type: %s', [DataTypeStr]);
end;
begin
// Load JSON string from file
JSONString := TFile.ReadAllText(AFileName);
// Parse JSON string
JSONArray := TJSONObject.ParseJSONValue(JSONString) as TJSONArray;
if not Assigned(JSONArray) then
raise Exception.Create(' Invalid JSON format in file.');
try
// Iterate through JSON array and populate the list
for i := 0 to JSONArray.Count - 1 do
begin
JSONObject := JSONArray.Items[i] as TJSONObject;
Name := JSONObject.GetValue< string>(' Name');
DataTypeStr := JSONObject.GetValue< string>(' DataType');
Value := JSONObject.GetValue< string>(' Value');
DefaultValue := JSONObject.GetValue< string>(' DefaultValue');
DataType := ParseDataType(DataTypeStr);
// Add the item
Self.Add(TDataItem.Create( Name, DataType, DefaultValue));
Self.Last.Value := Value; // Set the current value
end;
finally
JSONArray.Free;
end;
end;
end.

Zitat von Antwort:
Ihre Unit ist bereits klar strukturiert und für den Zweck ordentlich aufgebaut – dennoch lassen sich ein paar "Redundanzen vermeiden", einige Methoden und Konstrukte "vereinfachen", und kleine Verbesserungen sind sinnvoll.
Hier folgt eine "optimierte und kommentierte Version" Ihrer Unit. Am Ende finden Sie eine "Zusammenfassung" der Änderungen und Vorschläge.
Delphi-Quellcode:
unit Unit_TDataModule;
interface
uses
System.Classes, System.SysUtils, System.Generics.Collections, System.JSON,
System.IOUtils, TypInfo;
type
TInputType = (itString, itNumber, itBoolean);
TDataItem = class
private
FName: string;
FDataType: TInputType;
FValue: string;
FDefaultValue: string;
public
constructor Create( const AName: string; ADataType: TInputType; const ADefaultValue: string);
procedure ResetToDefault;
property Name: string read FName write FName;
property DataType: TInputType read FDataType write FDataType;
property Value: string read FValue write FValue;
property DefaultValue: string read FDefaultValue write FDefaultValue;
end;
TDataItemList = class(TObjectList)
private
function GetAsJSON: string;
class function ParseDataType( const DataTypeStr: string): TInputType; static;
public
property AsJSON: string read GetAsJSON;
procedure LoadFromStringList(AStringList: TStringList);
procedure SaveToFile( const AFileName: string);
procedure LoadFromFile( const AFileName: string);
end;
implementation
{ TDataItem }
constructor TDataItem.Create( const AName: string; ADataType: TInputType; const ADefaultValue: string);
begin
inherited Create;
FName := AName;
FDataType := ADataType;
FDefaultValue := ADefaultValue;
FValue := FDefaultValue; // Wert gleich beim Erzeugen auf Default setzen
end;
procedure TDataItem.ResetToDefault;
begin
FValue := FDefaultValue;
end;
{ Hilfsfunktion für DataType-Parsing }
class function TDataItemList.ParseDataType( const DataTypeStr: string): TInputType;
begin
// Erlaubt sowohl Enum-Namen als auch Klartext
if SameText(DataTypeStr, ' Boolean') or SameText(DataTypeStr, ' itBoolean') then
Result := itBoolean
else if SameText(DataTypeStr, ' Number') or SameText(DataTypeStr, ' itNumber') then
Result := itNumber
else if SameText(DataTypeStr, ' String') or SameText(DataTypeStr, ' itString') then
Result := itString
else
raise Exception.CreateFmt(' Unknown data type: %s', [DataTypeStr]);
end;
{ TDataItemList }
function TDataItemList.GetAsJSON: string;
var
JSONArray: TJSONArray;
DataItem: TDataItem;
begin
JSONArray := TJSONArray.Create;
try
for DataItem in Self do
JSONArray.AddElement(TJSONObject.Create
.AddPair(' Name', DataItem. Name)
.AddPair(' DataType', GetEnumName(TypeInfo(TInputType), Ord(DataItem.DataType)))
.AddPair(' Value', DataItem.Value)
.AddPair(' DefaultValue', DataItem.DefaultValue)
);
Result := JSONArray.ToJSON;
finally
JSONArray.Free;
end;
end;
procedure TDataItemList.LoadFromStringList(AStringList: TStringList);
var
LineParts: TArray;
DataType: TInputType;
i: Integer;
begin
Self.Clear;
for i := 0 to AStringList.Count - 1 do
begin
LineParts := AStringList[i].Split([' |']);
if Length(LineParts) <> 3 then
raise Exception.CreateFmt(' Invalid format in line %d: %s', [i+1, AStringList[i]]);
DataType := ParseDataType(LineParts[1]);
// Konstruktor setzt Value gleich auf Default
Self.Add(TDataItem.Create(LineParts[0], DataType, LineParts[2]));
end;
end;
procedure TDataItemList.SaveToFile( const AFileName: string);
begin
TFile.WriteAllText(AFileName, AsJSON);
end;
procedure TDataItemList.LoadFromFile( const AFileName: string);
var
JSONArray: TJSONArray;
DataItemJSON: TJSONObject;
JSONString: string;
i: Integer;
begin
Self.Clear;
JSONString := TFile.ReadAllText(AFileName);
JSONArray := TJSONObject.ParseJSONValue(JSONString) as TJSONArray;
if not Assigned(JSONArray) then
raise Exception.Create(' Invalid JSON format in file.');
try
for i := 0 to JSONArray.Count - 1 do
begin
DataItemJSON := JSONArray.Items[i] as TJSONObject;
if not Assigned(DataItemJSON) then continue;
Self.Add(TDataItem.Create(
DataItemJSON.GetValue(' Name'),
ParseDataType(DataItemJSON.GetValue(' DataType')),
DataItemJSON.GetValue(' DefaultValue')
));
// Wert ggf. setzen
Self.Last.Value := DataItemJSON.GetValue(' Value', Self.Last.DefaultValue);
end;
finally
JSONArray.Free;
end;
end;
end.

Zitat von Fortsetzung der Antwort:
Was wurde VEREINFACHT und VERBESSERT?
1. "Konsistenz & Redundanzen"
- "ParseDataType" als Klassenmethode einmal zentral statt dupliziert.
- "Value" schon im Konstruktor auf den Default gesetzt ('FValue := FDefaultValue'), Initialisierung an anderer Stelle (z.B. nach 'Add') ist überflüssig.
- Beim Laden aus JSON: Wert wird direkt gesetzt, wenn vorhanden, sonst Default.
2. "String-Vergleiche beim Typ-Parsing"
- Akzeptiert jetzt auch 'itNumber', 'itBoolean' usw., wie sie beim JSON-Speichern per 'GetEnumName' entstehen.
3. "Aufräumen"
- Keine doppelten oder unnötigen lokalen Variablen/Schritte.
- '.Clear' auf der Liste vor jedem Ladevorgang (aus Datei/Strings), damit die Liste sich sauber neu befüllt.
- Verwendung von '.AddPair'-Chaining bei JSON (kürzer, übersichtlicher).
4. "Kommentare und Lesbarkeit"
- Nur noch da, wo sie Mehrwert bieten.
5. "Sonstige Kleinigkeiten"
- Fehlerbehandlung wie in Ihrem Original beibehalten.
- Keine leeren Methoden.
"Zusammenfassung"
"Redundanzen:"
- Separate ParseDataType-Implementierungen eliminiert.
- Keine doppelte Initialisierung von Value.
"Optimierung:"
- Zentrale Utility-Funktion für Typ-Parsing.
- Konstruktor setzt Value wie Default.
- JSON-Handling und Datenimport schlanker.
"Fazit:"
Der Code ist so noch kompakter, wartbarer und robuster.
Wenn Sie spezielle Wünsche haben (z.B. Delimiter-Option, weitere Datentypen): gerne melden!
Was mich stört ist die Nutzung von with, deshalb hab' ich die Units noch ohne with erstellen lassen:

Zitat von Frage:
Bei uns wird die Nutzung von with kategorisch abgelehnt. Könnten Sie bitte beide Units in Versionen ohne with zur Verfügung stellen?

Zitat von Antwort:
Natürlich! Hier sind die beiden Units jeweils komplett "ohne Verwendung des with-Statements".
Ich habe alle relevanten Codeabschnitte entsprechend angepasst.
# 1. "Unit_TDataModule" (Daten- und Listentypen)
'Bereits vorher wurde in dieser Unit kein' with 'verwendet. Zur Klarheit führe ich die aktuell optimierte Version dennoch explizit auf:'
Delphi-Quellcode:
unit Unit_TDataModule;
interface
uses
System.Classes, System.SysUtils, System.Generics.Collections, System.JSON,
System.IOUtils, TypInfo;
type
TInputType = (itString, itNumber, itBoolean);
TDataItem = class
private
FName: string;
FDataType: TInputType;
FValue: string;
FDefaultValue: string;
public
constructor Create( const AName: string; ADataType: TInputType; const ADefaultValue: string);
procedure ResetToDefault;
property Name: string read FName write FName;
property DataType: TInputType read FDataType write FDataType;
property Value: string read FValue write FValue;
property DefaultValue: string read FDefaultValue write FDefaultValue;
end;
TDataItemList = class(TObjectList)
private
function GetAsJSON: string;
class function ParseDataType( const DataTypeStr: string): TInputType; static;
public
property AsJSON: string read GetAsJSON;
procedure LoadFromStringList(AStringList: TStringList);
procedure SaveToFile( const AFileName: string);
procedure LoadFromFile( const AFileName: string);
end;
implementation
{ TDataItem }
constructor TDataItem.Create( const AName: string; ADataType: TInputType; const ADefaultValue: string);
begin
inherited Create;
FName := AName;
FDataType := ADataType;
FDefaultValue := ADefaultValue;
FValue := FDefaultValue;
end;
procedure TDataItem.ResetToDefault;
begin
FValue := FDefaultValue;
end;
class function TDataItemList.ParseDataType( const DataTypeStr: string): TInputType;
begin
if SameText(DataTypeStr, ' Boolean') or SameText(DataTypeStr, ' itBoolean') then
Result := itBoolean
else if SameText(DataTypeStr, ' Number') or SameText(DataTypeStr, ' itNumber') then
Result := itNumber
else if SameText(DataTypeStr, ' String') or SameText(DataTypeStr, ' itString') then
Result := itString
else
raise Exception.CreateFmt(' Unknown data type: %s', [DataTypeStr]);
end;
function TDataItemList.GetAsJSON: string;
var
JSONArray: TJSONArray;
DataItem: TDataItem;
Obj: TJSONObject;
begin
JSONArray := TJSONArray.Create;
try
for DataItem in Self do
begin
Obj := TJSONObject.Create;
Obj.AddPair(' Name', DataItem. Name);
Obj.AddPair(' DataType', GetEnumName(TypeInfo(TInputType), Ord(DataItem.DataType)));
Obj.AddPair(' Value', DataItem.Value);
Obj.AddPair(' DefaultValue', DataItem.DefaultValue);
JSONArray.AddElement(Obj);
end;
Result := JSONArray.ToJSON;
finally
JSONArray.Free;
end;
end;
procedure TDataItemList.LoadFromStringList(AStringList: TStringList);
var
LineParts: TArray;
DataType: TInputType;
i: Integer;
begin
Self.Clear;
for i := 0 to AStringList.Count - 1 do
begin
LineParts := AStringList[i].Split([' |']);
if Length(LineParts) <> 3 then
raise Exception.CreateFmt(' Invalid format in line %d: %s', [i+1, AStringList[i]]);
DataType := ParseDataType(LineParts[1]);
Self.Add(TDataItem.Create(LineParts[0], DataType, LineParts[2]));
end;
end;
procedure TDataItemList.SaveToFile( const AFileName: string);
begin
TFile.WriteAllText(AFileName, AsJSON);
end;
procedure TDataItemList.LoadFromFile( const AFileName: string);
var
JSONArray: TJSONArray;
DataItemJSON: TJSONObject;
JSONString: string;
i: Integer;
Name, DataTypeStr, Value, DefaultValue: string;
begin
Self.Clear;
JSONString := TFile.ReadAllText(AFileName);
JSONArray := TJSONObject.ParseJSONValue(JSONString) as TJSONArray;
if not Assigned(JSONArray) then
raise Exception.Create(' Invalid JSON format in file.');
try
for i := 0 to JSONArray.Count - 1 do
begin
DataItemJSON := JSONArray.Items[i] as TJSONObject;
if not Assigned(DataItemJSON) then continue;
Name := DataItemJSON.GetValue(' Name');
DataTypeStr := DataItemJSON.GetValue(' DataType');
DefaultValue := DataItemJSON.GetValue(' DefaultValue');
Value := DataItemJSON.GetValue(' Value', DefaultValue);
Self.Add(TDataItem.Create( Name, ParseDataType(DataTypeStr), DefaultValue));
Self.Last.Value := Value;
end;
finally
JSONArray.Free;
end;
end;
end.

Zitat von Fortsetzung der Antwort:
# 2. "StudyDatamodule" (UI-/Form- Unit, jetzt explizit ohne with)
Delphi-Quellcode:
unit StudyDatamodule;
interface
uses
System.SysUtils, System.Classes, Math, FMX.Types, FMX.Controls, FMX.Forms,
Unit_TDataModule; // Annahme: Ihre eigene Unit mit TDataItemList, TDataItem, TInputType
type
TForm1 = class(TForm)
mmo1: TMemo;
btnInitList: TButton;
edtPropertyValue: TEdit;
lblPropertyName: TLabel;
btnUp: TButton;
btnDown: TButton;
edtDatatype: TEdit;
btnListAsJSON: TButton;
procedure FormCreate(Sender: TObject);
procedure btnUpClick(Sender: TObject);
procedure btnDownClick(Sender: TObject);
procedure edtPropertyValueExit(Sender: TObject);
procedure edtPropertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
procedure btnListAsJSONClick(Sender: TObject);
private
FDataItemList: TDataItemList;
FItemIndex: Integer;
procedure SetItemIndex(AIndex: Integer);
procedure DisplayActiveItem( Index: Integer);
procedure SaveInputToItem;
function ValidateInput( const Value: string; DataType: TInputType): Boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R '.fmx}
procedure TForm1.FormCreate(Sender: TObject);
var
StringList: TStringList;
DataItem: TDataItem;
begin
StringList := TStringList.Create;
FDataItemList := TDataItemList.Create(True);
try
// Demo-Daten
StringList.Add(' EnableLogging|Boolean|true');
StringList.Add(' MaxLogFileSize|Number|1048576');
StringList.Add(' LogFilePath|String|C:\Logs\app.log');
StringList.Add(' EnableAutoSave|Boolean|true');
StringList.Add(' AutoSaveInterval|Number|10');
StringList.Add(' Theme|String|dark');
StringList.Add(' ShowNotifications|Boolean|true');
StringList.Add(' MaxRetries|Number|3');
StringList.Add(' RetryDelay|Number|5');
StringList.Add(' EnableEncryption|Boolean|true');
StringList.Add(' EncryptionKey|String|default_key');
StringList.Add(' AllowGuestAccess|Boolean|false');
StringList.Add(' DefaultLanguage|String|en');
StringList.Add(' TimeoutDuration|Number|30');
StringList.Add(' EnableDebugMode|Boolean|false');
FDataItemList.LoadFromStringList(StringList);
// Ausgabe zur Kontrolle
for DataItem in FDataItemList do
mmo1.Lines.Add(Format(' Name: %s, DataType: %s, DefaultValue: %s',
[DataItem. Name, GetEnumName(TypeInfo(TInputType), Ord(DataItem.DataType)), DataItem.DefaultValue]));
FItemIndex := 0;
if FDataItemList.Count > 0 then
DisplayActiveItem(FItemIndex);
finally
StringList.Free;
end;
end;
procedure TForm1.SetItemIndex(AIndex: Integer);
begin
if FDataItemList.Count = 0 then Exit;
FItemIndex := EnsureRange(AIndex, 0, FDataItemList.Count - 1);
DisplayActiveItem(FItemIndex);
end;
procedure TForm1.btnUpClick(Sender: TObject);
begin
SetItemIndex(FItemIndex + 1);
end;
procedure TForm1.btnDownClick(Sender: TObject);
begin
SetItemIndex(FItemIndex - 1);
end;
procedure TForm1.DisplayActiveItem( Index: Integer);
var
DataItem: TDataItem;
DataTypeName: string;
begin
if ( Index = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[ Index];
DataTypeName := GetEnumName(TypeInfo(TInputType), Ord(DataItem.DataType));
lblPropertyName.Text := DataItem. Name;
edtPropertyValue.Text := DataItem.Value;
edtDatatype.Text := DataTypeName;
end;
procedure TForm1.edtPropertyValueExit(Sender: TObject);
begin
SaveInputToItem;
end;
procedure TForm1.edtPropertyValueKeyDown(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
var
DataItem: TDataItem;
begin
if (FItemIndex = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[FItemIndex];
case DataItem.DataType of
itNumber:
if not (KeyChar in [' 0'..' 9', ' .', ' -', #8]) then KeyChar := #0;
itBoolean:
if not (KeyChar in [' t',' r',' u',' e',' f',' a',' l',' s',#8]) then KeyChar := #0;
end;
end;
procedure TForm1.SaveInputToItem;
var
DataItem: TDataItem;
UserInput: string;
begin
if (FItemIndex = FDataItemList.Count) then Exit;
DataItem := FDataItemList.Items[FItemIndex];
UserInput := Trim(edtPropertyValue.Text);
if not ValidateInput(UserInput, DataItem.DataType) then
begin
ShowMessage(Format(' Invalid input for %s. Expected a %s value.',
[DataItem. Name, GetEnumName(TypeInfo(TInputType), Ord(DataItem.DataType))]));
edtPropertyValue.SetFocus;
Exit;
end;
DataItem.Value := UserInput;
end;
function TForm1.ValidateInput( const Value: string; DataType: TInputType): Boolean;
var
TempFloat: Double;
TempBoolean: Boolean;
begin
case DataType of
itString:
Result := True;
itNumber:
Result := TryStrToFloat(Value, TempFloat);
itBoolean:
Result := TryStrToBool(Value, TempBoolean);
else
Result := False;
end;
end;
procedure TForm1.btnListAsJSONClick(Sender: TObject);
begin
mmo1.Lines.Add(FDataItemList.AsJSON);
end;
end.

Zitat von Fortsetzung der Antwort:
## Hinweise
- "with wurde nirgends verwendet."
- Alle Object-Properties werden nun immer explizit angesprochen.
- Die Units sind so sofort in Projekten ohne with-Verwendung einsetzbar.
Falls Sie weitere Wünsche zur Codegestaltung haben, geben Sie einfach Bescheid!
Leider hab' ich nur Delphi 7 und kann von daher nicht prüfen, ob die Überarbeitung von Perplexity.ai sinnvoll, kompilierbar und fehlerfrei ist.
|