Autor Beitrag
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 18694
Erhaltene Danke: 1620

W10 x64 (Chrome, IE11)
Delphi 10.2 Ent, Oxygene, C# (VS 2015), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 07.09.11 08:19 
Hallo,

ich habe zuletzt ein paar Lösungen für Objektabfragen in der Art von LINQ in Delphi gesehen. Dabei ist mir die Idee gekommen einfache Filter- und Sortierfunktionen typsicher zur Verfügung zu stellen. Denn ein Problem der Lösungen, die ich bisher gesehen habe, ist, dass der Compiler nicht prüfen kann, ob sie so korrekt sind.

Das Problem dabei:
Delphi unterstützt keine Eigenschaftsreferenzen. Daher ist es erst einmal nicht so einfach möglich die Eigenschaften an eine solche Abfrage zu übergeben.

Das habe ich durch Klassenmethoden gelöst, die in dem Zielobjekt die Eigenschaften auflösen. Diese können wiederum als Referenz übergeben werden.

Butter bei die Fische, so sieht das Ergebnis aus:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
  TfrmQueryListDemoMain = class(TForm)
  private
    FTestData: TQueryList<TTestClass>;
    FNoBananaChecks: TWhereArray<TTestClass>;
    FBOrGreaterChecks: TWhereArray<TTestClass>;
    FNameOrder: TOrderByArray<TTestClass>;
  end;

procedure TfrmQueryListDemoMain.FormCreate(Sender: TObject);
begin
  // init filter
  SetLength(FNoBananaChecks, 2);
  FNoBananaChecks[0] := TWhereClause<TTestClass>.Create(TTestClass.GetValue, wopLower, 500);
  FNoBananaChecks[1] := TWhereClause<TTestClass>.Create(TTestClass.GetName, wopNotEqual, 'Banane');
  SetLength(FBOrGreaterChecks, 1);
  FBOrGreaterChecks[0] := TWhereClause<TTestClass>.Create(TTestClass.GetName, wopGreaterEqual, 'B');
  // init order
  SetLength(FNameOrder, 1);
  FNameOrder[0] := TOrderByClause<TTestClass>.Create(TTestClass.GetName);
  // init data
  FTestData := TQueryList<TTestClass>.Create(True);
  FTestData.Add(TTestClass.Create('Mango'100));
  FTestData.Add(TTestClass.Create('Apfel'400));
  FTestData.Add(TTestClass.Create('Birne'600));
  FTestData.Add(TTestClass.Create('Banane'300));
end;

procedure TfrmQueryListDemoMain.FormDestroy(Sender: TObject);
begin
  FTestData.Free;
end;

procedure TfrmQueryListDemoMain.btnNoBananaClick(Sender: TObject);
var
  Current: TTestClass;
begin
  for Current in FTestData.SelectWhere(FNoBananaChecks).OrderBy(FNameOrder) do
    ShowMessage(Current.Name + '=' + IntToStr(Current.Value));
end;

procedure TfrmQueryListDemoMain.btnBOrGreaterClick(Sender: TObject);
var
  Current: TTestClass;
begin
  for Current in FTestData.SelectWhere(FBOrGreaterChecks).OrderBy(FNameOrder) do
    ShowMessage(Current.Name + '=' + IntToStr(Current.Value));
end;
Der Filter und die Sortiermethode werden also hier im Beispiel einmalig beim Start des Programms erstellt und danach nur noch benutzt. Wenn man diesen jetzt gute Namen gibt, versteht man das an der Stelle, an der das dann verwendet wird, sehr gut. Die Filter usw. kann man zentral verwalten und für mehrere Listen der Klasse gleichermaßen verwenden.

Jetzt fehlt noch die Testklasse, die ich hier verwendet habe:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  TTestClass = class
  private
    FName: string;
    FValue: Integer;
  public
    class function GetName(AInstance: TTestClass): string;
    class function GetValue(AInstance: TTestClass): Integer;
    constructor Create(AName: string; AValue: Integer);
    property Name: string read FName write FName;
    property Value: Integer read FValue write FValue;
  end;

implementation

{ TTestClass }

constructor TTestClass.Create(AName: string; AValue: Integer);
begin
  FName := AName;
  FValue := AValue;
end;

class function TTestClass.GetName(AInstance: TTestClass): string;
begin
  Result := AInstance.Name;
end;

class function TTestClass.GetValue(AInstance: TTestClass): Integer;
begin
  Result := AInstance.Value;
end;
Das heißt es genügt für jede Eigenschaft, die für Filter oder Sortierungen benutzt werden soll, eine solche Klassenmethode zu schreiben.

Bisher gibt es auch nur die Möglichkeit zu filtern und zu sortieren und die Datentypen Double, Integer und String. Das ergänze ich noch ein wenig. Es wird aber nicht so viel möglich sein wie mit Lösungen, die mit SQL-ähnlichen Strings oder ähnlichem Arbeiten. Dafür ist das ganze typsicher, so dass Fehler nicht erst beim Debuggen entdeckt werden. Zudem sollte es schneller sein.

Die Unit steht wie immer wahlweise unter der MPL, LGPL oder GPL zur Verfügung.

Getestet nur mit XE und XE2, aber es könnte mit Delphi 2009 oder 2010 auch funktionieren.

Schönen Gruß,
Sebastian
Einloggen, um Attachments anzusehen!

Für diesen Beitrag haben gedankt: BenBE