[Gelöst] Schweinerei mit einer generischen Liste getrieben ... wird mir das auf die Füße fallen?

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

[Gelöst] Schweinerei mit einer generischen Liste getrieben ... wird mir das auf die Füße fallen?

Beitrag von Nimral »

Guten Abend,

ich spiele seit einigen Tagen mit Generics herum, und bin angemessen angetan von dem was sie mir bieten. Irgendwann kam ich auf die wilde Idee, ein Generic einer Variable zuweisen zu wollen, bzw. eine Funktion zu haben die ein Generic zurückgibt. Die Motivation kam eigentlich aus dem Debugging, erst einmal habe ich an hundert Stellen den ItemIndex der RadioGroup mittels case ausgewertet und dann die jeweils selektierte Liste angesprochen, dann dachte ich, wär doch nett, wenn ich dafür eine Property oder Funktion einsetzen könnte, die das an einer Stelle abfackelt und mir die richtige Liste in die Hand gibt. Ich habe dann dieses kleine Testprogramm zusammengenagelt:

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  ComCtrls, FGL;

type
  TFPGListString = specialize TFPGList<ShortString>;
  TFPGListStringOne = class(TFPGListString);
  TFPGListStringTwo = class(TFPGListString);

var
  FPGListStringOne : TFPGListStringOne;
  FPGListStringTwo : TFPGListStringTwo;

[....]

procedure TForm1.DumpList(const AList:TFPGListString);

var
  S:ShortString;

begin
Memo1.Lines.Clear;
for S in AList do
   Memo1.Lines.Add(S);
end;

procedure TForm1.Button2Click(Sender: TObject);

var
  S: ShortString;

begin
  S := DateTimeToStr(Now());
  CurrentList.Add(S);
  DumpList(CurrentList);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FPGListStringOne := TFPGListStringOne.Create;
  FPGListStringOne.add('One');
  FPGListStringTwo := TFPGListStringTwo.Create;
  FPGListStringTwo.add('Two');
end;

[...]

procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
begin
DumpList(CurrentList);
end;

function TForm1.CurrentList: TFPGListString;

begin
  case RadioGroup1.ItemIndex of
  0: result := FPGListStringOne;
  1: result := FPGListStringTwo;
  end;
end;

Ursprünglich wollte ich natürlich CUrrentList so schreiben, dass es eine TFPGList<String> zurückgibt, aber da macht der Compiler nicht mit. Also habe ich mich daran erinnert, dass TFPGList ein Abkömmling eines Objekts ist, und hab dann wie ihr oben seht mehr so nach Gefühl einen generischen Type of String definiert, und von ihm die beiden Listen als Klassen abgeleitet. Sieh an, das Demo-Programm läuft ausgezeiochnet: den generischen Listen hat das offenbar nicht geschadet, sie funktionieren nach wie vor, und ich konnte meine CurrentList Funktion bekommen.

Habe ich mich da innerhalb der Specs von Geenerics bewegt, oder bin ich gerade dabei, fürchterlich aufs Glatteis zu gehen?

Thnx, Armin.
Dateianhänge
GenericAsAVariable.zip
(106.29 KiB) 41-mal heruntergeladen
Zuletzt geändert von Nimral am Sa 10. Jul 2021, 10:15, insgesamt 1-mal geändert.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Schweinerei mit einer generischen Liste getrieben ... wird mir das auf die Füße fallen?

Beitrag von Socke »

Nimral hat geschrieben:
Sa 10. Jul 2021, 00:57
Habe ich mich da innerhalb der Specs von Geenerics bewegt, oder bin ich gerade dabei, fürchterlich aufs Glatteis zu gehen?
Du kannst spezialisierte Klassen genau so wie normale Klassen weiter vererben. Daher passt das schon.
Wenn du nur eine Klasse ableitetst, ist es auch möglich, die spezialisierung anonym durchzuführen:

Code: Alles auswählen

type
  TFPGListStringOne = class(specialize TFPGList<ShortString>);
An deinem Code stellt sich die Frage, warum du zwei weitere Listenklassen brauchst, ohne dort weitere Methoden/Eigenschaften zu definieren - es sei denn, dies ist einem Minimalbeispiel geschuldet.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Nimral
Beiträge: 390
Registriert: Mi 10. Jun 2015, 11:33

Re: Schweinerei mit einer generischen Liste getrieben ... wird mir das auf die Füße fallen?

Beitrag von Nimral »

Morgen, Socke,

Ja, es ist nur eine Minimaldemo um zu sehen, ob das so prinzipiell klappt.

Danke fürs Mitdenken und für die Info, dass das so geht, und die raffinierte Variante mit der Spezialisierung der einen Klasse! Die Variante kannte ich (wie so vieles bei Generics und bei OOP) noch nicht. Hut ab vor den Leuten, die einen Compiler für diesen Himalaya aus Syntax geschaffen haben.

HG, Armin

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: [Gelöst] Schweinerei mit einer generischen Liste getrieben ... wird mir das auf die Füße fallen?

Beitrag von PascalDragon »

Also erstmal, wie Socke schon sagt, ist dein Code richtig. Du leitest ja nur ab und die Spezialisierung ist die gemeinsame Elternklasse. Das ist da ganz genau so wie in normaler OOP.

Wenn du in der gleichen Unit bist, dann kannst du auch getrost MyType = class(specialize SomeGenType<SomeParam>) mehrfach nehmen, da der Compiler die Spezialisierung nur einmal durchführt. Über mehrere Units hinweg sind es jedoch verschiedene Typen und du solltest dann einfach die Spezialsierung separat in einer dritten Unit ablegen (also type MySpezType = specialize SomeGenType<SomeParam>) und diese dann als Elternklasse verwenden wie du es ja in deinem Code gemacht hast.

Was ich jedoch nicht weiß, ist warum dein Code mit dem inline specialize nicht gehen soll:

Code: Alles auswählen

program tgenres;

{$mode objfpc}{$H+}

uses
  fgl;

type
  TList1 = class(specialize TFPGList<String>);
  TList2 = class(specialize TFPGList<String>);

  TTest = class
    fList1: TList1;
    fList2: TList2;
    fIndex: LongInt;
    constructor Create;
    function CurrentList: specialize TFPGList<String>;
  end;

{ TTest }

constructor TTest.Create;
begin
  fList1 := TList1.Create;
  fList2 := TList2.Create;
end;

function TTest.CurrentList: specialize TFPGList<String>;
begin
  case fIndex of
    0:
      Result := fList1;
    1:
      Result := fList2;
    otherwise
      Result := Nil;
  end;
end;

var
  l: specialize TFPGList<String>;
  t: TTest;
begin
  t := TTest.Create;
  try
    l := t.CurrentList;
  finally
    t.Free;
  end;
end.
Hast du ein minimales Beispiel, das nicht funktioniert?
FPC Compiler Entwickler

Antworten