Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Wie dynamischer Vorfahr für generische Klasse? (https://www.delphipraxis.net/149136-wie-dynamischer-vorfahr-fuer-generische-klasse.html)

himitsu 15. Mär 2010 10:42


Wie dynamischer Vorfahr für generische Klasse?
 
Praktisch möchte ich sowas haben,
Delphi-Quellcode:
TMyClass<Ancestor: Class> = Class(Ancestor)
*                                 ^^^^^^^^
aber Delphi meint nur
Zitat:

[DCC Fehler] ...: E2021 Klassentyp erwartet
Denn diese Klasse soll selber nur eine gewisse Funktionalität für andere, davon abgeleitete Klassen bereitstellen, aber ich wollte den anderen Klassen nicht die Möglichkeit nehmen, jeweil einen "anderen" Vorfahren zu nutzen.


z.B.
Delphi-Quellcode:
TTest = Class(TMyClass<TComponent>)
wobei es quasi dem entspricht, nur daß eben meine gen. Klasse hier noch mit dazwischen eingefügt wird.
Delphi-Quellcode:
TTest = Class(TComponent)

Uwe Raabe 15. Mär 2010 10:48

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Ich fürchte, da wirst du zur Zeit kein Glück haben. Mach dafür doch ein Feature-Request in QC auf.

Eventuell kannst du das aber mit Aggregation oder einem Decorator lösen.

himitsu 15. Mär 2010 11:06

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Nee, andere Wege helfen hier leider nichts,
denn wenn z.B. TMyClass direkt von TObject abgeleitet ist (alles Andere wäre Aufgrund des "unbekannten" Basistyps nicht sinvoll),
dann hätte auch TTest nur TObject als Vorfahre und wäre dann, laut dem Beispiel, nicht mit TComponent kompatibel und somit kann man diese Klasse auch nicht als "Komponente" einsetzen.

shmia 15. Mär 2010 11:30

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von himitsu
Denn diese Klasse soll selber nur eine gewisse Funktionalität für andere, davon abgeleitete Klassen bereitstellen...

Was wäre das konkret?
Ich kann mir nicht so richtig vorstellen, welche Funktionalität auf beliebige Klassen vererbt werden soll.
Welche Funktionalität ist so allgemein nützlich, dass man sie im Prinzip in jeder Klasse brauchen kann?
(etwa Logging oder Persistenz der Objektdaten?)

HERMES 15. Mär 2010 11:30

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Man kann auch Typschranken für Generics angeben, das müsste dann aber so aussehen

Delphi-Quellcode:
TMyClass<A:TAncestor> = Class(TAncestor)
Das legt fest, dass alle Klassen mit denen du deinen generische Klasse initialisierst vom Typ TAncester oder eine davon abgeleitete Klasse sein muss.

Wenn du class als Schranke angibst kannste du das auch gleich weglassen.


Zitat:

Denn diese Klasse soll selber nur eine gewisse Funktionalität für andere, davon abgeleitete Klassen bereitstellen, aber ich wollte den anderen Klassen nicht die Möglichkeit nehmen, jeweil einen "anderen" Vorfahren zu nutzen.
Ich weis nicht ob ich dich richtig verstanden habe, aber du hast doch dann immer noch das "Problem", dass du TAncester statisch angeben musst.

EDIT: Aber andereseits, wenn du nur Funktionalität für Klassen die von der Klasse selbst abgeleietet sind anbieten möchtest brauchst du doch garkeine generics, da ein Obertyp der Klasse bekannst ist und die dynamische Bindung den Rest erledigen sollte.

himitsu 15. Mär 2010 11:39

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Neee, det "Basistyp ist eben hier noch nicht festgelegt, dieser ist in der generischen Klasse noch unbekannt und wird erst in der Ableitung entschieden.

Ich wollte auf diese Weise eine allgemeine Verwaltung einer verketteten Liste implementieren.
Bei dieser Verwaltung ist der "Basistyp" unwichtig.

Das : Class soll nur sicherstellen, daß dort nur irgendeine Klasse angegeben werden kann,
aber welche Klasse das nun sein soll, ist ja nicht wichtig.

Uwe Raabe 15. Mär 2010 11:47

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Und was spricht gegen sowas?

Delphi-Quellcode:
type
  TListNode<T> = class
  private
    FData: T;
    FNext: TListNode<T>;
    FPrev: TListNode<T>;
  public
    property Data: T read FData write FData;
    property Next: TListNode<T> read FNext write FNext;
    property Prev: TListNode<T> read FPrev write FPrev;
  end;

himitsu 15. Mär 2010 12:05

Re: Wie dynamischer Vorfahr für generische Klasse?
 
@Uwe:
Delphi-Quellcode:
var X: TListNode<TStringList>;

if X is TStringList then // geht nicht, da keine Stringliste
Das ist halt mein Problem.
Ich möchte den Basistypen ändern und nicht irgendeinen inneren Typen
und dafür muß man den Basistypen von TListNode ändern.
Delphi-Quellcode:
TListNode<T> = class(Basistyp)

uligerhardt 15. Mär 2010 12:11

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Schaut wie das hier aus. ;)

Uwe Raabe 15. Mär 2010 12:13

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von himitsu
Delphi-Quellcode:
var X: TListNode<TStringList>;

if X is TStringList then // geht nicht, da keine Stringliste

Klar, aber dann schreib doch einfach

Delphi-Quellcode:
if X.Data is TStringList then

himitsu 15. Mär 2010 12:27

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von Uwe Raabe
Klar, aber dann schreib doch einfach ...

OK, dann eben ein anderes Beispiel:
Delphi-Quellcode:
procedure Proc(SL: TStrings);
begin
end;

var X: TListNode<TStringList>;

Proc(X); // geht auch nicht, da keine Stringliste
Und sag jetzt nicht,
Zitat:

Klar, aber dann schreib doch einfach
Delphi-Quellcode:
Proc(X.data);

denn ich will ja das ganze X übergeben können und nicht nur den einen Teil des "Vorfahren".
Außerdem finde ich es mit den .data im Code dann etwas unübersichtlich/umständlich.

Stevie 15. Mär 2010 12:39

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Ich frag mich gerade, wie die Implementierung deiner Klasse aussehen soll, wenn du dort nichtmal weißt, wovon sie abgeleitet ist. Dazu müsstest du doch zumindest einen Constraint angeben, oder nicht?

himitsu 15. Mär 2010 12:46

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von Stevie
Ich frag mich gerade, wie die Implementierung deiner Klasse aussehen soll, wenn du dort nichtmal weißt, wovon sie abgeleitet ist. Dazu müsstest du doch zumindest einen Constraint angeben, oder nicht?

Für die Verwaltung ist nur der aktuelle Typ wichtig und nicht irgendein Vorfahr. (zumindestens in meinem Fall)

HERMES 15. Mär 2010 12:57

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Wenn du sagst, deine Basisklasse ist nicht bekannt, dann meist du damit, dass diese auch eine generische Klasse ist? Oder wie istdas sonst zu verstehen


Zitat:

Klar, aber dann schreib doch einfach ...
Delphi-Quellcode:
procedure Proc(SL: TListNode<TStringList>);
begin
end;

var X: TListNode<TStringList>;

Proc(X); // geht auch nicht, da keine Stringliste
oder allgemeiner
Delphi-Quellcode:
procedure Proc(SL: TListNode<Y>);
begin
end;

var X: TListNode<TStringList>;

Proc(X); // geht auch nicht, da keine Stringliste

Stevie 15. Mär 2010 13:00

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von himitsu
Zitat:

Zitat von Stevie
Ich frag mich gerade, wie die Implementierung deiner Klasse aussehen soll, wenn du dort nichtmal weißt, wovon sie abgeleitet ist. Dazu müsstest du doch zumindest einen Constraint angeben, oder nicht?

Für die Verwaltung ist nur der aktuelle Typ wichtig und nicht irgendein Vorfahr. (zumindestens in meinem Fall)

Mit Vorfahr meinte ich den Typparameter T bei deinem eingangs erwähnten Konstrukt, welcher ja der Vorfahr dieser Klasse wäre:
Delphi-Quellcode:
TMyClass<Ancestor: class> = class(Ancestor)
Welche Gemeinsamkeiten hätte denn TMyClass<TForm> mit TMyClass<TFoo> (bewusst nicht definierte Klasse gewählt) außer, dass beide explizit auf mindestens TMyClass<TObject> umgecastet werden könnten? Beziehungsweise, wozu muss der Typparameter der Vorfahr der Klasse sein? Nur, damit du ein Object davon an eine Methode übergeben kannst, die TForm bzw TFoo akzeptiert? Das geht auch anders.

Phoenix 15. Mär 2010 13:06

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von himitsu
Denn diese Klasse soll selber nur eine gewisse Funktionalität für andere, davon abgeleitete Klassen bereitstellen, aber ich wollte den anderen Klassen nicht die Möglichkeit nehmen, jeweil einen "anderen" Vorfahren zu nutzen.

Dafür sind Generics aber nicht gedacht.
Was Du haben willst sind Interfaces. Und um Methoden zur Verfügung zu stellen benutzt Du dann Extension Methods (Class helper) auf diesem Interface.

Somit können alle von ihrer gewünschten Basisklasse ableiten, das Interface implementieren (das kann auch einfach leer sein, wenn es nur um Methoden geht) und aufgerufen werde diese Methoden über die Class helper auf dem Interface.

himitsu 15. Mär 2010 13:33

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von Phoenix
Dafür sind Generics aber nicht gedacht.

Och menno. :cry:

Nja, ich hab mir hier halt ein Problem geschaffen
und versuche dafür nun eine "nette" Lösung zu finden.


Nja, ich werde noch etwas rumspielen ... mal sehn, vielleicht finde ich ja noch eine andere Lösung, außer dem Interface.



Ich spiele grad so ein bissl rum und probiere mehreres aus.

So war ich erstmal bei den "kleineren" Records gelandet, aber so wie es aussieht, werde ich wohl oder übel auf Klassen umsteigen müssen, weswegen ich dann auf das oben genannte "Problem" gekommen bin.

Delphi-Quellcode:
PMyRec = ^TMyRec<Typ: record>; // <<< geht natürlich nicht
                                //     aber das war ja klar
TMyRec<Typ: Record> = record      
  type PTyp = ^Typ;            // <<< geht
  {...}
end;
Allerdings geht auch das nicht, obwohl hier Typ ja bekannt wäre.
Delphi-Quellcode:
TMyRec<Typ: record> = record
  type PMyRec = ^TMyRec<Typ>; // <<< geht nicht
    PTyp = ^Typ;              // <<< geht
  {...}
end;
Wie soll man da einen Pointer deklarieren, welchen auch der generische Teil kennt?

OK, außer man macht es umständlicher extern,
welches aber nicht unbedingt eine "fehlerunanfälligere" Deklaration erfordert.
Delphi-Quellcode:
TMyRec<Typ: record; PRec> = record
  type PMyRec = PRec;
    PTyp = ^Typ;
  {...}
end;

PTest = ^TTest;
TTest = TMyRec<Integer, PTest>;
Und da Records keine Vererbung kennen, kann man hier ja keinen genaueren Typen festlegen, welcher dann ein "Value" kennt:
Delphi-Quellcode:
TMyRec<Typ: record> = record
  x: Typ;
  function Test: Integer;
end;

function TMyRec<Typ>.Test: Integer;
begin
  Result := X.Value; // <<< geht nicht
end;
Selbst wenn sichergestellt ist, daß der Record "Typ" einen Wert "Value" besietzt, geht dieses nicht,
da der Compiler dennoch meckert, daß "Value" nicht bekannt sei.

[add]
Und Generics für Helper gehn leider auch nicht.
Delphi-Quellcode:
TMyRec<Typ: Record> = Record Helper for Typ
  ...
end;

TMyCls<Typ: Class> = Class Helper for Typ
  ...
end;

Phoenix 15. Mär 2010 14:30

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Wieso bist Du so auf Generics versessen?
Die haben zwar ihre Daseinsberechtigung, sind aber kein Allheilmittel.

Was willst Du denn eigentlich *genau* machen bzw. welches Problem hast Du Dir geschaffen was Du 'nett' Lösen willst?
Vielleicht finden wir einen anderen / besseren Lösungsansatz der nicht das vergewaltigen von Sprachkonstrukten zum Inhalt hat? ;-)

himitsu 15. Mär 2010 15:14

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Zitat:

Zitat von Phoenix
Wieso bist Du so auf Generics versessen?

Ich bin ein Playboy ... äääääää, ein Spieljunge/Spielkind
und "neue" Dinge müssen gründlich ausprobiert werden.

Ja, und ich suche gern die Grenze des Möglichen und wenn möglich überschreite ich sie gerne mal.

Wenn es nicht klappt, dann geht's halt nicht und ich löse es anders/normal. :angel2:


Die Verwaltung einer mehrfach verketteten Liste ist ja nicht unbedingt sooooo einfach.
Für sowas hab ich jetzt erstmal ein Template, welches man verwenden/anpassen könnte.
Nun wollte ich das Ganze aber mal versuchen generisch zu lösen, so daß man es direkt einbinden und ohne Änderung verwenden könnte.

Uwe Raabe 15. Mär 2010 15:54

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Ist nur meine Meinung, aber ich halte es für keine gute Idee, die strukturelle Verknüpfung von Objekten in die Klassen zu verlagern. Das schränkt die Verwendbarkeit m.E. zu stark ein.

Beispiel: Nehmen wir die Klasse TStringList und leiten daraus einen TStringListNode ab, der dann für die verkettete Liste zuständig ist. Natürlich kann ich jetzt sowas wie Node is TStringList abfragen, aber was ist mit einer von TStringList abgeleiteten Klasse (z.B. TExtendedStringList)? Diese müsste eine neue Node-Ableitung bekommen TExtendedStringListNode. Das könnte wohl auch der Anlass zu deiner Frage gewesen sein.

Nehmen wir nun mal an, es gäbe diesen Node-Generic. Nun brauchst du aber die Objekte nicht in einer verketten Liste, sondern in einer Baumstruktur. Geht aber nicht, weil du nicht einfach aus den ListNode-Instanzen TreeNode-Instanzen machen kannst.

Wenn du die Struktur-Elemente aus den Klassen heraus hälst, gewinnst du wesentlich mehr Spielraum (Playboy!).

himitsu 15. Mär 2010 22:15

Re: Wie dynamischer Vorfahr für generische Klasse?
 
Liste der Anhänge anzeigen (Anzahl: 4)
*ahhhhhhhhhhh* manchmal könnte man einfach nur schreien :wall:

Hab mal alles etwas umgestellt,
der Compiler meint es ginge jetzt,
aber beim Erstellen eines Testprojektes,
meint er urplötzlich nach dem Testcode (hier sogar nach dem END. der DPR)
Delphi-Quellcode:
[DCC Fataler Fehler] Project4.dpr(23): F2084 Interner Fehler: AV21F90C39-W00000014-1
Eigentlich hätte ja sowas ausgereicht, aber da es keine Möglichkeit gibt PTheRecord zu dereferenzieren...
Delphi-Quellcode:
TDoubleLinkedList<PTheRecord {: ^Record}> = Record
Nja, und Pointer ist keine gültige Beschränkung.
Delphi-Quellcode:
TDoubleLinkedList<PTheRecord: Pointer> = Record

Wenn ich aber nur den Record übergebe und den Pointer intern deklariere,
Delphi-Quellcode:
TDoubleLinkedList<TheRecord: Record> = Record
Private
  Type PTheRecord = ^TheRecord;
dann schlägt im Testcode die stenge Typenprüfung von Delphi zu
Delphi-Quellcode:
[DCC Fehler] Project4.dpr(19): E2010 Inkompatible Typen: 'PMyRec' und 'TDoubleLinkedList<TMyRec>.PTheRecord'


So, den Rest der Woche werde ich wohl keine, bzw. nicht genug Zeit haben, um (richtig) zu Programmieren. :?
Also mal sehn wann/ob ich hier wieder dazukomme weiterzumachen.


[edit]
OK, wenn man auf die Idee kommt, den internen Pointer rauszulassen, dann geht es urplötzlich :stupid:
Delphi-Quellcode:
TMyRec = Record
  List: TDoubleLinkedList<TMyRec>;
  Data: Integer;
End;
PMyRec = TDoubleLinkedList<TMyRec>.PRecord;
anstatt
Delphi-Quellcode:
PMyRec = ^TMyRec;
TMyRec = Record
  List: TDoubleLinkedList<TMyRec, PMyRec>;
  Data: Integer;
End;


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:23 Uhr.

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