z.B. statt TList<> und TStringList verwende ich zunehmend lieber ein TArray<String>, sowie eben Records anstatt Klassen. (sind teilweise einfacher als die doppelte Deklaration eines Interfaces)
und dennoch kann man so von der automatischen Speicherverwaltung profitieren.
(seit den Custom-Managed-Records sind nun auch die letzten Probleme mit "unsicheren" Typen behebbar, also Pointer und Objekte innerhalb des Reocrds, sowie die einfachen dynamischen Arrays, welchen leider seit Jahrzehnten das CopyOnWrite fehlt, entgegen z.B. einem String, der intern auch nur ein aufgemotztes Char-Array ist, aber bei Welchem das CopyOnWrite funktioniert)
Delphi-Quellcode:
type
TMyRecord = record
private
FList: TArray<Integer>;
public
class function Create: TMyRecord; static;
function Add(a: Integer): TMyRecord:
function Send;
function Clear;
end;
class function TMyRecord.Create: TMyRecord;
begin
Result := Self;
end;
function TMyRecord.Add(a: Integer): TMyRecord:
begin
FList := FList + [a];
end;
function TMyRecord.Send;
begin
// mach was mit FList
end;
function TMyRecord.Clear;
begin
FList := []; // oder FList := nil; oder SetLength(FList, 0);
end;
TMyRecord.Create.Add(123).Add(456).Send;
var R: TMyRecord;
R.Add(123);
R.Add(456);
R.Send;
R.Clear; // damit die 123 und 456 verschwinden (oder beim Send, z.B. via Parameter, nach dem Senden automatisch leeren lassen)
R.Add(789);
R.Send;
R.Add(123).Add(456).Send;
R.Clear;
R.Add(789).Send;
Delphi-Quellcode:
type
TSendInputHelper = record
private
FData: TArray<TInput>; // oder eben dein TInputArray
class function MergeInputs(InputsBatch: array of TInputArray): TInputArray; static;
public
class function ConvertShiftState(ClassesShiftState: System.Classes.TShiftState): TSIHShiftState; static;
...
procedure AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean); overload;
procedure AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean); overload;
...
end;
bzw.
Delphi-Quellcode:
function AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean): TSendInputHelper; overload;
function AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean): TSendInputHelper; overload;
...
end;
---
protected
gibt es nicht, da es leider keine Recordvererbung gibt.
Sinnvoll ist somit eh nur
private
und
public
.
class function
können nur müssen
static
sein, wodurch es dann auch kein Self gibt. (ohne Vererbung gibt es in einer Funktion eh immer nur den einen "Klassen"-Typ)
class function ConvertShiftState(ClassesShiftState: System.Classes.TShiftState): TSIHShiftState; static;
Das in TMyRecord.Add, nennt sich "string-like Operator", sowie Insert und Delete gibt es auch seit 'ner Weile.
https://docwiki.embarcadero.com/RADS...Dynamic_Arrays
Ebenso weitere Array-Helper (
unit System.Generics.Collections)
Delphi-Quellcode:
FList := [456, 123, 789];
TArray.Sort<Integer>(FList);
if TArray.BinarySearch<Integer>(FList, 456, idx) then
ShowMessage(idx.ToString); // = '1'
BinarySearch geht aber nur für sortierte Arrays.
Für TArray<String> gäbe es auch
IndexStr und
IndexText.
Constructoren für Record sind zu praktisch, aber die müssen leider immer Parameter besitzen (vermutlich eine kranke Kompatibilität zu C++ oder so), aber
Delphi-Quellcode:
type
TMyRecord = record
constructor Create; // [dcc32 Fehler] E2394 Parameterlose Konstruktoren sind für Record-Typen nicht zulässig
constructor Create(Value: Integer);
constructor Create(Values: array of Integer); overload; // bzw. (Values: TArray<Integer>)
end;
TMyRecord = record
class function Create: TMyRecord; static; // der Bugfix für den E2394
end;
Das alte TPoint kennst'e doch noch?
Delphi-Quellcode:
TPoint = record
X: Integer;
Y: Integer;
end;
neu sieht es so aus
Delphi-Quellcode:
TPoint = record
X: FixedInt;
Y: FixedInt;
public
constructor Create(P : TPoint); overload;
constructor Create(const X, Y : Integer); overload;
//operator overloads
class operator Equal(const Lhs, Rhs : TPoint) : Boolean;
class operator NotEqual(const Lhs, Rhs : TPoint): Boolean;
class operator Add(const Lhs, Rhs : TPoint): TPoint;
class operator Subtract(const Lhs, Rhs : TPoint): TPoint;
class operator Implicit(Value: TSmallPoint): TPoint;
class operator Explicit(Value: TPoint): TSmallPoint;
class function PointInCircle(const Point, Center: TPoint; const Radius: Integer): Boolean; static; inline;
/// <summary> Zero point having values of (0, 0). </summary>
class function Zero: TPoint; inline; static;
function Distance(const P2 : TPoint) : Double;
procedure SetLocation(const X, Y : Integer); overload;
procedure SetLocation(const P : TPoint); overload;
procedure Offset(const DX, DY : Integer); overload;
procedure Offset(const Point: TPoint); overload;
function Add(const Point: TPoint): TPoint;
function Subtract(const Point: TPoint): TPoint;
function IsZero : Boolean;
function Angle(const APoint: TPoint): Single;
end;
Delphi-Quellcode:
var Demo: TArray<TPoint>; // oder : array of TPoint;
Demo := [TPoint.Create(1, 2), TPoint.Create(3, 4)];
Demo := Demo + [TPoint.Create(5, 6)];
Insert(TPoint.Create(7, 8), Demo, 3);
Viele Records haben keinen Constructor und Delphi generiert auch (noch) keinen Standard-Constructor.
TInput leider auch, aber das kann man noch nachrüsten:
Delphi-Quellcode:
type
TInputHelper = record helper for TInput
constructor Create(P: TMouseInput); overload;
constructor Create(P: TKeybdInput); overload;
constructor Create(P: THardwareInput); overload;
end;
// bzw.
TInputHelper = record helper for TInput
constructor Create(dx, dy: LongInt; mouseData, dwFlags: DWORD); overload;
//constructor Create(dx, dy: LongInt; mouseData, dwFlags, time: DWORD; dwExtraInfo: ULONG_PTR); overload;
constructor Create(wVk, wScan: WORD; dwFlags: DWORD); overload;
//constructor Create(wVk, wScan: WORD; dwFlags, time: DWORD; dwExtraInfo: ULONG_PTR); overload;
//constructor Create(P: THardwareInput); overload;
end;
PS: Record-Helper für sowas wie Enum und Set geht noch nicht so, aber man könnte sich einen Record mit AutoCasts basteln.
Also TShiftStateEx als Parameter für deine Funktionen. Dann brauchst'e das Overload nicht mehr, da sich die beiden Sets größtenteils automatisch von und dahin casten, bei Zuweisung := , sowie bei Übergabe an den Parameter.
Zu geil wäre es natürlich, wenn man stattdessen TSIHShiftState so anpassen könnte, dass TShiftState zuweisungskompatibel würde. (aber sowas erwarte ich frühestens im nächsten Jahrtausend von Embarcadero)
Delphi-Quellcode:
TShiftStateEx = record
private
FState: TSIHShiftState;
function GetShiftState: TShiftState; // Result := TShiftState(FState - ssWin);
procedure SetShiftState(Value: TShiftState); // FState := TSIHShiftState(Value);
public
class operator Implicit(a: TSIHShiftState): TShiftStateEx; // Result.FState := a; -> entspricht TheRecord := SIHShiftState;
class operator Implicit(a: TShiftState): TShiftStateEx; // Result.FState := TSIHShiftState(a); -> entspricht TheRecord := ShiftState;
class operator Implicit(a: TShiftStateEx): TShiftState; // Result := TShiftState(a.FState - ssWin); -> entspricht ShiftState := TheRecord;
class operator Explicit(a: TShiftStateEx): TSIHShiftState; // Result := a.FState; -> entspricht SIHShiftState := TSIHShiftState(TheRecord);
// Und noch ein paar kleine Hilfsfunktionen, falls man nicht alles direkt über harte oder implizite Casts machen will:
property AsShiftState: TShiftState read GetShiftState write SetShiftState;
property AsSIHShiftState: TSIHShiftState read FState write FState;
function HasMenu: Boolean; // Result := ssWin in FState;
end;
Delphi-Quellcode:
// anstatt
procedure AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean); overload;
procedure AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean); overload;
procedure AddShortCut(ShiftState: TSIHShiftState; ShortChar: Char); overload;
procedure AddShortCut(ShiftState: TSIHShiftState; ShortVK: Word); overload;
procedure AddShortCut(ShiftState: System.Classes.TShiftState; ShortChar: Char); overload;
procedure AddShortCut(ShiftState: System.Classes.TShiftState; ShortVK: Word); overload;
// nur noch
procedure AddShift(ShiftState: TShiftStateEx; Press, Release: Boolean); overload;
procedure AddShortCut(ShiftState: TShiftStateEx; ShortChar: Char); overload;
procedure AddShortCut(ShiftState: TShiftStateEx; ShortVK: Word); overload;