--- name: delphi-modern description: Generate correct, modern Delphi 13 code following contemporary best practices. Use when writing Delphi/Object Pascal code, creating VCL or FireMonkey applications, or helping with Delphi-specific programming tasks. Focuses on safe, maintainable patterns while avoiding deprecated and dangerous constructs. --- # Modern Delphi 13 Coding Standards ## Core Principles Write correct, maintainable Delphi 13 code that follows modern best practices. Avoid deprecated patterns, dangerous constructs, and practices that bloat binary size. ## Memory Management **Always use FreeAndNil instead of Free:** ```pascal // Correct FreeAndNil(MyObject); // Avoid MyObject.Free; ``` **Benefits of FreeAndNil:** - Sets reference to nil after freeing, preventing use-after-free bugs - Makes dangling pointer issues immediately apparent - Safer in exception scenarios ## Forbidden Constructs **Never use these deprecated or dangerous patterns:** - `absolute` keyword - Use proper variable references instead - Raw pointers (`^`, `Ptr^`) - Use object references and dynamic arrays - `TFile` - Use TFileStream, TStreamReader/Writer, or TFile class methods - `Application.ProcessMessages` - Use threads or async patterns - `with` statement - Causes ambiguity, use explicit references - Generics when simpler alternatives exist - They increase binary size **Instead of pointers, use:** ```pascal // Use dynamic arrays instead of pointer arrays var Items: TArray; // Use object references instead of object pointers var MyList: TList; // Not TList^ ``` **Instead of Application.ProcessMessages:** ```pascal // Use TThread for background work TThread.CreateAnonymousThread( procedure begin // Background work here end ).Start; // Or use TTask from System.Threading TTask.Run( procedure begin // Background work end ); ``` **Instead of TFile (old unit):** ```pascal // Use TFile class methods (from System.IOUtils) var Content: string; begin Content := TFile.ReadAllText('file.txt'); TFile.WriteAllText('output.txt', Content); end; // Or use streams var Stream: TFileStream; begin Stream := TFileStream.Create('file.dat', fmOpenRead); try // Work with stream finally FreeAndNil(Stream); end; end; ``` ## Generics Usage **Avoid generics when simpler alternatives exist** - they increase binary size significantly. **Use generics only when:** - Type safety is critical - You need multiple strongly-typed collections - The pattern is truly reusable across many types **When generics are justified:** ```pascal // Type-safe dictionary when you need key-value pairs var Cache: TDictionary; ``` ## Modern Patterns **Use try-finally for resource management:** ```pascal var Stream: TFileStream; Reader: TStreamReader; begin Stream := TFileStream.Create('file.txt', fmOpenRead); try Reader := TStreamReader.Create(Stream); try // Use reader finally FreeAndNil(Reader); end; finally FreeAndNil(Stream); end; end; ``` **Use System.IOUtils for file operations:** ```pascal uses System.IOUtils; // Check if file exists if TFile.Exists('config.ini') then // ... // Read all lines var Lines: TArray; begin Lines := TFile.ReadAllLines('data.txt'); end; // Directory operations var Files: TArray; begin Files := TDirectory.GetFiles('C:\Data', '*.txt'); end; ``` **Use anonymous methods and closures:** ```pascal // Event handlers Button1.OnClick := procedure(Sender: TObject) begin ShowMessage('Clicked'); end; // Background processing TTask.Run( procedure var Result: Integer; begin Result := PerformCalculation; TThread.Synchronize(nil, procedure begin Label1.Caption := Result.ToString; end ); end ); ``` **Reduce usage of string helpers and new RTL features:** ```pascal var Text: string; begin // String helpers (Delphi 10.1+) if Text.IsEmpty then Exit; Text := Text.ToUpper; Text := Text.Replace('old', 'new'); // Split strings var Parts := Text.Split([',', ';']); end; ``` These new features are cool BUT they are not debugable. ## VCL/FireMonkey Best Practices ## Exception Handling **Use specific exception types:** ```pascal try // Risky operation except on E: EFileNotFoundException do ShowMessage('File not found: ' + E.Message); on E: EAccessViolation do ShowMessage('Access violation: ' + E.Message); on E: Exception do ShowMessage('Unexpected error: ' + E.Message); end; ``` **Don't swallow exceptions silently:** ```pascal // Bad - hides problems try RiskyOperation; except // Silent failure end; // Good - log or handle appropriately try RiskyOperation; except on E: Exception do begin LogError(E); raise; // Re-raise if caller should know end; end; ``` ## Code Quality Guidelines **Use meaningful names:** ```pascal // Good var CustomerList: TStringList; TotalAmount: Currency; IsValid: Boolean; // Avoid var List1: TStringList; X: Currency; Flag: Boolean; ``` **Keep procedures and functions focused:** ```pascal // Each procedure should do one thing well procedure LoadCustomerData(CustomerId: Integer); procedure ValidateCustomerData(const Data: TCustomerData); procedure SaveCustomerData(const Data: TCustomerData); ``` **Use constants and enumerations:** ```pascal const MAX_RETRY_COUNT = 3; DEFAULT_TIMEOUT = 5000; type TConnectionState = (csDisconnected, csConnecting, csConnected, csError); ``` ## Common Patterns **Avoid initialization and finalization sections:** Avoide because these sections are executed in non-deterministic order. **Property declarations:** ```pascal private FName: string; FAge: Integer; procedure SetName(const Value: string); public property Name: string read FName write SetName; property Age: Integer read FAge write FAge; ``` **Interface-based programming:** ```pascal type IDataProvider = interface ['{GUID-HERE}'] function GetData: string; procedure SaveData(const AData: string); end; TFileDataProvider = class(TInterfacedObject, IDataProvider) // Reference counted automatically function GetData: string; procedure SaveData(const AData: string); end; ```