Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Callback-Funktion für TObjectList.Sort (https://www.delphipraxis.net/157539-callback-funktion-fuer-tobjectlist-sort.html)

HJay 15. Jan 2011 16:12


Callback-Funktion für TObjectList.Sort
 
Mir gelingt es nicht, die Callbackfunktion als Teil der Klasse zu definieren, zu der sie jeweils gehört. Wenn ich einfach im implementation-Teil die Callbackfunktion definiere, klappt alles auf Anhieb. Es wäre bei vielen verschiedenen Sortierroutinen für viele verschiedene von TObjectList abgeleitete Klassen ja aber eigentlich wünschenswert, die Compare-Funktionen in die Klasse zu integrieren. Man kann aber keine Methode statt des Prozedur-Pointers angeben.

Wie löst Ihr so etwas? Gibt es eine geradlinige Lösung, auf die ich nicht komme?

Code:
function CompareKey1(Item1, Item2: TMyObject):Integer; // OK

function MyObjectList.CompareKey1(Item1, Item2: TMyObject):Integer; // geht nicht

procedure MyObjectList.MySort;
begin
  Sort(@CompareKey1);
end;

Deep-Sea 15. Jan 2011 16:35

AW: Callback-Funktion für TObjectList.Sort
 
Wenn ich dein Problem richtig verstanden habe, musst du deine Funktion wohl als statische Klassenmethode deklarieren:
Delphi-Quellcode:
MyObjectList = class(...)
  ...
  class function CompareKey1(Item1, Item2: TMyObject): Integer; static;
  ...
end;

HJay 15. Jan 2011 17:42

AW: Callback-Funktion für TObjectList.Sort
 
Code:
class function CompareKey1(Item1, Item2: TObject): Integer; static;

Error: "Field definition not allowed after methods or properties."
Compiliert leider nicht...?!

Ich habe mich inzwischen übrigens so beholfen, dass ich den QuickSort-Algorithmus aus Classes.pas kopiert habe und ihn als neue QuickSortByMethod()-Methode direkt in mein TMyObjectList implementiert habe. Diese Methode nimmt jetzt auch Compare-Methoden an und nicht nur normale Funktionen. Dann habe ich gleich noch eine SortOrder ergänzt, so dass das ganz praktisch ist.

Dennoch, es scheint so zu sein, als ob ausgerechnet eine so wichtige Klasse wie TList nicht vollständig objektorientiert implementiert wurde (zumindest in Delphi 6, das ich verwende). Eigentlich ziemlich seltsam.

Aber vielleicht gibt es ja doch eine Lösung, die ich nicht erkenne...

Frei herumfliegende Funktionen quasi gedanklich einer bestimmten Klasse zuzuordnen, kommt mir wirklich merkwürdig vor. Haben die sich das wirklich so gedacht?

Namenloser 15. Jan 2011 19:38

AW: Callback-Funktion für TObjectList.Sort
 
Zitat:

Zitat von HJay (Beitrag 1074913)
Code:
class function CompareKey1(Item1, Item2: TObject): Integer; static;

Error: "Field definition not allowed after methods or properties."
Compiliert leider nicht...?!

Klingt eher so, als ob du die Methode an der falschen stelle deklariert hättest. Methoden müssen immer nach Feldern stehen.
Richtig:
Delphi-Quellcode:
TFoobar = class
  A,B,C: integer;
  D: string;
  procedure MyMethod;
end;
Falsch:
Delphi-Quellcode:
TFoobar = class
  A,B,C: integer;
  procedure MyMethod;
  D: string;
end;
Falsch:
Delphi-Quellcode:
TFoobar = class
  procedure MyMethod;
  A,B,C: integer;
  D: string;
end;

Thom 15. Jan 2011 19:57

AW: Callback-Funktion für TObjectList.Sort
 
"Classic"-Compiler mögen diese Deklaration nicht. :wink:
Also mit anderen Worten: static ist kein Schlüsselwort, sondern wird als Feldname innerhalb des Objektes interpretiert. Daher die - völlig korrekte - Fehlermeldung!

Es geht aber doch (gerade probiert mit Delphi 5) - mit einem ganz tiefen Griff in die Trickkiste:
Delphi-Quellcode:
type
  TMyObject = class(...)
    ...
  end;

  TMyObjectList = class(TObjectList)
  public
    procedure MySort;
  published
    {class} function CompareKey1(Item1{, Item2}: TMyObject): Integer;
  end;

{class} function TMyObjectList.CompareKey1(Item1{, Item2}: TMyObject): Integer;
begin
  Result:=Mein_Vergleich(TMyObject(self),Item1); //!!!
end;

procedure TMyObjectList.MySort;
begin
  Sort(MethodAddress('CompareKey1'));
end;
Und es funktioniert tatsächlich! 8-)

Trick 1:
Beim Aufruf von Sort in der Methode MySort wird lediglich die Methodenadresse übergeben - also wie bei einer herkömmlichen Prozedur/Funktion. Da es sich dabei nur um einen Zeiger handelt, akzeptiert das der Compiler ohne Widerspruch.
Wichtig: Damit die Methodenadresse gefunden werden kann, muss sie als published deklariert werden - ansonsten muss man RTTI-Mechanismen bemühen.

Trick 2:
Da ist es bei CompareKey1 trotz Lüge an den Compiler um eine Methode handelt, gibt es beim Aufruf einen "unsichtbaren" Parameter - nämlich self. Der ist bei herkömmlichen Objektmethoden mit einem Zeiger auf das Objekt belegt - bei Klassenmethoden eben mit der Klasse, in der sich die Methode befindet. Ich "mißbrauche" ihn einfach zur Übergabe des ersten Items. Das geht gut, weil es sich in allen Fällen um Zeiger handelt. Und da ist es völlig egal, was darin enthalten ist.

Namenloser 15. Jan 2011 20:05

AW: Callback-Funktion für TObjectList.Sort
 
Das ist aber alles andere als schön... dann würd ich lieber eine externe Prozedur verwenden. Man kann ja als Präfix "TMyObjectList_" verwenden, sodass sie fast aussieht wie eine echte Methode.

Thom 15. Jan 2011 20:26

AW: Callback-Funktion für TObjectList.Sort
 
Zitat:

Zitat von NamenLozer (Beitrag 1074946)
Das ist aber alles andere als schön... dann würd ich lieber eine externe Prozedur verwenden. Man kann ja als Präfix "TMyObjectList_" verwenden, sodass sie fast aussieht wie eine echte Methode.

:gruebel: :wiejetzt:
Es ging doch um die Frage, ob es prinzipiell möglich ist, Prozeduren und Funktionen in Objekte zu "verlegen". Die eindeutige und kurze Antwort dazu lautet: Ja!
Die Frage war nicht, ob der Quellcode schön aussieht. Programmierer von bösen Tierchen achten wahrscheinlich auch weniger auf "Schönheit", Eleganz und akademische Lehrdoktrinen als mehr auf Funktionalität... :witch:

Außerdem: Was stört Dein Schönheitsempfinden? Dass ein Parameter "fehlt"!?
Hast Du Dir schon einmal die Unit ObjAuto angeschaut? Wahrscheinlich nicht - denn dann hättest Du einen Eindruck davon, was "unschön" aussieht und wieviel Arbeit Entwickler dafür investieren, komplizierte Zusammenhänge für Klick-Mich-Zusammen-Hobby-Programmierer so aufzubereiten, damit diese "schönen" Code schreiben können... :thumb:

HJay 16. Jan 2011 17:19

AW: Callback-Funktion für TObjectList.Sort
 
@Thom: Danke für Deine Mühe. Da habe ich wirklich etwas gelernt, auch wenn ich zugeben muss, dass mir das etwas zuviel Getrickse ist. Aber interessant! Und schön kurz!

Wie gesagt, ich habe jetzt eine eigene Sort-Routine geschrieben, die eben ganz "legal" Methoden annimmt. War letztlich der einfachste Weg für mich und schön übersichtlich.

Ich schließe aber daraus, dass es tatsächlich so ist, dass TList es nicht vorsieht, Methoden zu verwenden. Ganz schön dämlich und altmodisch gelöst, oder?

Uwe Raabe 16. Jan 2011 17:39

AW: Callback-Funktion für TObjectList.Sort
 
Zitat:

Zitat von HJay (Beitrag 1075089)
Ganz schön dämlich und altmodisch gelöst, oder?

Altmodisch? Nun ja, unter der aktuellen Delphi XE wird TObjectList eigentlich gar nicht mehr benutzt. Als modernen Ersatz gibt es da eine generische TObjectList<T>, die eben auch typsicher ist. Als Vergleichsvehikel gibt man dort ein IComparer<T> Interface an, für das es die verschiedensten Implementierungen gibt - unter anderem auch eine mit anonymen Methoden. Wirklich altmodisch?

Ach ja, die statische Klassenmethode geht natürlich auch - aber halt noch nicht unter Delphi 6 (ist schließlich auch schon 10 Jahre her)


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:11 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz