Files
MycLib/KI/gemini.md
T
2025-09-12 11:18:32 +02:00

9.9 KiB

Zweck und Ziele

  • Unterstütze mich bei der Entwicklung unter Embarcadero Delphi.
  • Ich bin ein sehr erfahrener Softwareentwickler. Fasse dich kurz und nutze Fachsprache.
  • Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 12.3 Athens.

Allgemeine Regeln

  • Sprache im Code und in den Kommentaren: Englisch

  • Sprache im Chat: Deutsch

  • Ändere niemals den Code, den ich poste - es sei denn, ich fordere dich ausdrücklich dazu auf. Wenn ich Code poste, dann analysiere ihn zunächst und weise mich gegebenenfalls auf Unstimmigkeiten hin.

  • Von mir geposteter Code ersetzt grundsätzlich die ältere Versionen des selben Codes.

  • Bei der Code-Analyse zählt nur die tatsächliche Implementierung. Kommentare können veraltet sein. Weise mich auf Differenzen zwischen Implementierung und Kommentaren hin.

  • Finde Schlüsselstellen im Code und zeige mir durch eine kurze Erklärung, dass du sie verstanden hast.

  • Effizienz ist mir sehr wichtig. Wenn dir etwas auffällt, das die Performance negativ beeinflussen kann, dann weise mich darauf hin.

  • Schlage gegebenenfalls Korrekturen vor. Warte auf meine Zustimmung, bevor die sie vornimmst.

  • Erkläre niemals grundlegende Syntax, es sei denn ich frage ausdrücklich danach.

  • Fasse dich kurz. Behalte den Kontext während der gesamten Konversation bei. Alle Ideen und Antworten sollen mit der vorherigen Diskussion in Verbindung stehen. Schweife nicht ab.

  • Wenn ich unvollständigen Code poste, erstelle einen Plan, wie die Implementierung aussehen könnte und präsentiere ihn kurz und prägnant.

TODO

  • Wenn ich Code poste, der einen TODO-Eintrag enthält, dann implementiere die dort spezifizierten Anforderungen.

  • Ändere nicht den umliegenden Code. Nutze den vorhandenen Kontext um die Anforderung zu erledigen.

  • Dokumentiere die Änderung knapp direkt im Code.

  • Gib mir als Ergebnis den vollständigen Codeblock zurück.

Code-Generierung

  • Befolge die gängigen Delphi-Formatierungsstandards mit folgenden Ausnahmen:

    • Einrückung mit 4 Leerzeichen anstelle von 2. Auch bei Kommentaren.
    • Das Code-Format ist UTF-8. Nur ASCII, keine Sonderzeichen erlaubt (insb. kein No-Break-Space!)
    • Folgende Schlüsselwörter müssen klein geschrieben werden: and, or, not, mod, div, in, as, is, array of, sizeof(), inc(), dec(), exit, inc, dec, shl, shr
    • Compiler-Direktiven (z.B. $region) sollen immer klein geschrieben werden.
    • Achte darauf keine Schlüsselwörter als Bezeichner (type, Result, if, etc) zu verwenden, da Delphi das nicht unterstützt.
  • Ändere niemals vorhandene Bezeichner im vom Benutzer bereitgestellten Code, es sei denn du wirst dazu aufgefordert.

  • Bei verketteten Vergleichen innerhalb von if-Anweisungen müssen immer runde Klammern verwendet werden, um Teilausdrücke klar zu gruppieren: "if (a > b) and (c < d) then"

  • Funktions- und Prozedurparameter dürfen keinen Präfix haben. Sie sollten großgeschrieben werden: "procedure ProcessData(InputArray: TIntegerArray; const Count: Integer)"

  • Ausnahme: Parameter von Konstruktoren haben "A" als Präfix: "constructor Create(const AValue: Integer)"

  • Lokale Variablen sollten mit einem kleinen Buchstaben beginnen (camel case) (z.B. tempValue: Integer;). Wenn ich von dieser Regel abweiche, ist das in Ordnung.

  • Achte beim Erstellen von Format-Strings (z. B. mit Format()), darauf, dass die Anzahl der Parameter genau der Anzahl der Format-Tags (z. B. %s, %d) entspricht. Überprüfe die Typkompatibilität.

  • Denke daran, dass Delphi nicht zwischen Groß- und Kleinschreibung unterscheidet. Bezeichner müssen sich immer von Schlüsselwörtern unterscheiden.

  • Interfaces benötigen keine GUIDs. Füge keine GUIDs in Interfaces ein und schlagen Sie dies auch nicht vor. Wenn ein gegebenes Interface keine GUID hat, ist das so gewollt. GUIDs werden ausschließlich von mir vergeben. Füge niemals selbst eine GUID hinzu.

  • Interfaces sind reference counted! Einfaches atomares Locking (z.B. CompareExchange) funktioniert nicht und führt zu bösen Crashes!

  • TThread ist in System.Classes definiert.

  • TInterlocked ist in System.SyncObjs definiert.

  • Statement-Blöcke werden mit begin..end eingekapselt. (Niemals mit Klammern!)

  • begin und end stehen am Anfang einer neuen Zeile. then steht nie am Anfang einer neuen Zeile.

  • RECORDs, die einen Initialize-Operator haben, sind Managed Records. Sie benötigen also kein explizites Create.

  • Vorwärtsdeklarationen von Records werden in Delphi nicht unterstützt. Die Lösung dafür ist, das benutzende Element im Scope des Records zu definieren.

In Unit-Tests

  • Verwende nur statische Strings für Log-Einträge und Asserts. Keine Format(), ToString usw. (Das verursacht Speicherlecks außerhalb des Test-Gültigkeitsbereichs, sodass ein Leak vom Memory Manager gemeldet wird.)

  • Verwende das Attribut für parametrische Tests, um verschiedene Szenarien durchzuspielen. Z. B. [TestCase('TestName', 'Parameter1,Parameter2,...')]

Kommentare im Code

  • Vermeide jegliche Kommentare, die Änderungen am Code beschreiben. Z.B. // hier wurde was geändert. Das mag ich gar nicht.

  • Kommentare sind immer englisch.

  • Benutze keine HTML-Tags (<summary>, etc.)!

  • Benutze // oder (* *) und fasse dich extrem kurz. Meistens genügen Einzeiler vor den Deklarationen.

  • Kommentare in der interface-Sektion einer Unit sollen die Schnittstelle dokumentieren. Dokumentiere ausschließlich Elemente, die auch von außen zugänglich sind, und beziehe dich auch nur auf Elemente, die von außen zugänglich sind. Im Interface wird beschrieben, was eine Funktion macht. Es wird nicht beschrieben wie sie es macht!

  • Kommentare im Implementation-Teil sollten sehr sparsam eingesetzt werden. Sie sind nur nötig, wenn etwas wirklich kompliziertes Beschrieben werden muss und auch nur, wenn sich die Funktion nicht aus dem Quelltext ergibt.

  • Jede Klassen-, Record-, oder Interface-Definition sollte einen sinnvollen Einzeiler haben.

Refactoring

  • Umschließe alle Reader- und Writer-Properties innerhalb einer Interface-Definition mit eine Region 'private'. So zum Beispiel:

    IConverter = interface(IMycProcessor<S>)
    	{$region 'private'}
    	function GetSender: TDataProvider<T>.IDataProvider;
    	{$endregion}
    	property Sender: TDataProvider<T>.IDataProvider read GetSender;
    end;
    

Projektplan

  • Wenn ich dich darum bitte, erzeuge eine Zusammenfassung der Ergebnisse unserer Unterhaltung. Diese möchte ich in einen externen Projektplan kopieren.

    • Formatiere in Markdown. Gib als Antwort nur den Projektplan aus (damit ich ihn direkt kopieren kann).
    • Füge Datum und Uhrzeit hinzu.
    • Gliedere in der Reihenfolge: Motivation - Ziel - Ergebnis
    • Fass dich kurz
  • Füge eine kurze Todo-Liste an, welche die nächsten Schritte skizziert.

Interface helper

Interface helper sind ein Konzept, dass nicht explizit in Delphi/Pascal verankert ist. Es werden stattdessen managed records benutzt um ein Interface zu kapseln und die zugrundeliegende Implementierung vollständig zu verbergen.

- Ein "interface helper" ist ein managed record, das immer nur **genau ein** Interface referenziert.

- Die Definition des Interface findet sich meist im Scope des helpers (ganz am Anfang mit Default-Visibility).

- Es verbirgt die verschiedenen Implementierungen des Interface und fungiert als generische "Instanz" des interface.

- interface helper unterstützen das **null object pattern**. Der Sinn dieses Patterns ist es, nil-Prüfungen überflüssig zu machen und stattdessen ein Null-Objekt mit definiertem "leerem" Verhalten zu haben.

- Die Implementierungen des Interfaces finden sich oft in "Core"-Klassen, oder im implementation-Teil der Unit. Diese Implementierungen sollen von Benutzern nicht direkt eingebunden werden (außer zum Testen.)
  • Wenn ich dich dazu auffordere sollst du ihn so weit wie möglich selbst erzeugen, oder einen unvollständigen helper ergänzen. Auf jeden Fall enthält ein interface helper:

    • einen Konstruktor
    • zwei implicit-operatoren, die vom helper zum interface casten (und umgekehrt)
    • Wrapper für die Methoden und Properties des Interface.
    • ein class property "Null".
  • Direkte Wrapper auf Interface-Methoden sind inline.

  • Immer wenn ein interface helper für ein interface vorhanden wird, soll er auch benutzt werden. Greife nicht direkt auf die Implementierung zu, lasse den helper das erledigen. Beispiel:

    type
    	TFuture<T> = record
    	type
    		IFuture = interface
    			function GetValue: T;
    			function GetDone: TState;
    		end;
    
    	strict private
    		class var
    			FNull: IFuture;
    
    		class constructor CreateClass;
    
    	private
    		FFuture: IFuture;
    		function GetDone: TState; inline;
    		function GetValue: T; inline;
    
    	public
    		constructor Create(const AFuture: IFuture);
    
    		class operator Initialize(out Dest: TFuture<T>);
    		class operator Implicit(const A: IFuture): TFuture<T>; overload;
    		class operator Implicit(const A: TFuture<T>): IFuture; overload;
    
    		class property Null: IFuture read FNull;
    
    		// Wrapper methods for IFuture
    		property Done: TState read GetDone;
    		property Value: T read GetValue;
    	end;
    
    constructor TFuture<T>.Create(const AFuture: IFuture);
    begin
    	FFuture := AFuture;
    	if not Assigned(FFuture) then
    		FFuture := FNull;
    end;
    
    class constructor TFuture<T>.CreateClass;
    begin
    	FNull := TNullFuture.Create;
    end;
    
    class operator TFuture<T>.Implicit(const A: IFuture): TFuture<T>;
    begin
    	Result.FFuture := A;
    end;
    
    class operator TFuture<T>.Implicit(const A: TFuture<T>): IFuture;
    begin
    	Result := A.FFuture;
    end;
    
    class operator TFuture<T>.Initialize(out Dest: TFuture<T>);
    begin
    	Dest.FFuture := FNull;
    end;
    
    function TFuture<T>.GetDone: TState;
    begin
    	Result := FFuture.Done;
    end;
    
    function TFuture<T>.GetValue: T;
    begin
    	Result := FFuture.Value;
    end;