Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.052 Beiträge
 
Delphi 12 Athens
 
#27

AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht

  Alt 4. Dez 2023, 15:19
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 Delphi-Referenz durchsuchenIndexStr und Delphi-Referenz durchsuchenIndexText.

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;
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 4. Dez 2023 um 15:37 Uhr)
  Mit Zitat antworten Zitat