Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Typumwandlung mit Generika (https://www.delphipraxis.net/131674-typumwandlung-mit-generika.html)

Panthrax 28. Mär 2009 19:35


Typumwandlung mit Generika
 
Ich habe keine Möglichkeit es zu probieren, interessiere mich aber trotzdem für die Funktionstüchtigkeit folgender Methodenaufrufe:
Delphi-Quellcode:
program Typumwandlung;

{$AppType Console}

uses
  SysUtils,
  Classes;

type
  TGenericClassA<T> = class
    public
    procedure Method1; virtual;
  end;
  TGenericClassB<T> = class(TGenericClassA<T>)
    public
    procedure Method1; override; virtual;
  end;
  TClassA = TGenericClassA<TObject>;
  TClassB = TGenericClassB<TObject>;

procedure TGenericClassA<T>.Method1;
begin
  WriteLn('A: TGenericClassA<T>.Method1 aufgerufen.');
end;

procedure TGenericClassB<T>.Method1;
begin
  WriteLn('B: TGenericClassB<T>.Method1 aufgerufen.');
end;

procedure Test;
var
  B: TClassB;
begin
  B:=TClassB.Create;
  try
    { B }

    WriteLn('B.Method1;');
    B.Method1;

    WriteLn('TClassB(B).Method1;');
    TClassB(B).Method1;

    WriteLn('TGenericClassB<TObject>(B).Method1;');
    TGenericClassB<TObject>(B).Method1;

    WriteLn('(B as TClassB).Method1;');
    (B as TClassB).Method1;

    WriteLn('(B as TGenericClassB<TObject>).Method1;');
    (B as TGenericClassB<TObject>).Method1;

    { A }

    WriteLn('TClassA(B).Method1;');
    TClassA(B).Method1;

    WriteLn('TGenericClassA<TObject>(B).Method1;');
    TGenericClassA<TObject>(B).Method1;

    WriteLn('(B as TClassA).Method1;');
    (B as TClassA).Method1;

    WriteLn('(B as TGenericClassA<TObject>).Method1;');
    (B as TGenericClassA<TObject>).Method1;
  finally
    B.Free;
  end;
end;

begin
  Test;
end.
Kann mir jemand sagen, was da heraus kommt?

himitsu 29. Mär 2009 09:52

Re: Typumwandlung mit Generika
 
virtual; bei TGenericClassB<T> weggemacht und am Ende noch ein ReadLn;,
dann kommt das raus:
Zitat:

B.Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TClassB(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TGenericClassB<TObject>(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TClassB).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TGenericClassB<TObject>).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TClassA(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TGenericClassA<TObject>(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TClassA).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TGenericClassA<TObject>).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
[add]
Prozedur in TGenericClassA<T> nicht virtual und dann überschrieben, kommt sowas raus:
Zitat:

B.Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TClassB(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TGenericClassB<TObject>(B).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TClassB).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TGenericClassB<TObject>).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
TClassA(B).Method1;
A: TGenericClassA<T>.Method1 aufgerufen.
TGenericClassA<TObject>(B).Method1;
A: TGenericClassA<T>.Method1 aufgerufen.
(B as TClassA).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.
(B as TGenericClassA<TObject>).Method1;
B: TGenericClassB<T>.Method1 aufgerufen.

Panthrax 29. Mär 2009 22:07

Re: Typumwandlung mit Generika
 
Danke! :thumb:

Ich finde es schon ein bisschen seltsam, dass sich B: TClassB nach TClassA typumwandeln lässt. Ich hatte gedacht, der Compiler verweigert die Typumwandlung "B as TClassA" mit "Inkompatible Typen". So reagiert er jedenfalls auf "StringList as TComponent". TClassA ist schließlich kein Vorfahr von TClassB. Allerdings ist TClassA inhaltsgleich mit der Vorfahrklasse TClassB = TGenericClassB<TObject> << TGenericClassA<TObject>, da TClassA = TGenericClassA<TObject> definiert wurde.

Die Laufzeitinformationen zu den Typen TClassA und bspw. einer dritten, konkreten, gleich definierten Klasse TClassC = class(TGenericClassA<TObject>) müssten sich doch zumindest im Klassennamen unterscheiden, und bilden damit unterschiedliche Klassen. Liege ich falsch? Ich weiß nicht, irgendwie scheint mir das gewöhnungsbedürftig.

I.A 29. Mär 2009 22:25

Re: Typumwandlung mit Generika
 
Funktioniert das nich erst ab Delphi 2009? Ihr habt als verwendete Delphi Version Delphi 7 Enterprise angegeben.

Oder geht das nur mit der .NET Verstion?

Ich jedenfalls habe neben D7 Personal auch Turbo Delphi Explorer. Dort habe ich das Testprojekt hinkopiert. In eine Konsolenanwendung rein. Unit Classes ergänzt. Test; zwischen BEGIN END. des Hauptprogrammes geschrieben.


Fehlermeldung:
[Pascal Fehler] Project1.dpr(9): E2029 '=' erwartet, aber '<' gefunden

Dort steht bei mir:

type
TGenericClassA<T> = class //das hier
public
procedure Method1; virtual;

Scheint so das Generics erst später in Delphi eingeführt wurden. Soviel ich weiß, ab Delphi 2009.

mkinzler 29. Mär 2009 22:31

Re: Typumwandlung mit Generika
 
Generics gibt es unter Win32 erst ab D2009

himitsu 29. Mär 2009 22:54

Re: Typumwandlung mit Generika
 
da Links steht meißtens das, was man hauptsächlich verwendet ... und man muß ja nicht nur ein Delphi in den Pfötchen halten :angel:

Zitat:

Zitat von Panthrax
Ich hatte gedacht, der Compiler verweigert die Typumwandlung "B as TClassA" mit "Inkompatible Typen".

So reagiert er jedenfalls auf "StringList as TComponent".

B und A sind ja verwandt ... is/as prüft ob die angegebene Klasse sich selber oder ein Vorfahre von sich ist.

TStringList > TStrings > TPersistent > TObject

also nur dieses hier geht
Delphi-Quellcode:
TStringList is TStringList
TStringList is TStrings
TStringList is TPersistent
TStringList is TObject
"TStringList is TComponent" geht nicht, da es kein Vorfahre ist und demnach TStringList keine Eigenschaften von TComponent besitzt.


[add]
andersrum geht nur dieses nicht (ungültige Typumwandlung)
Delphi-Quellcode:
procedure Test;
var
  A: TClassA;
begin
  A := TClassA.Create;
  try
    ...

    WriteLn('(A as TClassB).Method1;');
    (A as TClassB).Method1;

    WriteLn('(A as TGenericClassB<TObject>).Method1;');
    (A as TGenericClassB<TObject>).Method1;
  finally
    A.Free;
  end;
  ReadLn;
end;

Khabarakh 29. Mär 2009 23:58

Re: Typumwandlung mit Generika
 
Zitat:

Zitat von Panthrax
TClassA ist schließlich kein Vorfahr von TClassB.

Doch, du legst schließlich nur Type Synonyms an. Da passiert in etwas soviel wie bei
Delphi-Quellcode:
type TFoo = TObject
nämlich überhaupt nichts. Bei
Delphi-Quellcode:
TClassB is TClassA
sieht der Compiler also nur
Delphi-Quellcode:
TGenericClassB<TObject> is TGenericClassA<TObject>
Im Kompilat sind TClassA und TClassB völlig verschwunden.

Panthrax 30. Mär 2009 00:28

Re: Typumwandlung mit Generika
 
Sebastian hat es eigentlich schon gesagt. Trotz rotem Kasten:

Das StringList-Beispiel ist aus der Welt ohne Generika. Dort kann man eine Variable nicht in einen Typ umwandeln, der außerhalb der Vorfahrlinie steht ("StringList as TComponent"). In der Welt mit Generika scheint (schien!) aber genau das zu funktionieren. Nachdem es sich bei mir etwas gesetzt hat, ist bei mir, glaube ich, der Groschen gefallen. Der folgende Quelltext soll nur noch einmal meine Überlegung verdeutlichen (Win32 ohne Generika, siehe TClassA1 und TClassA2):
Delphi-Quellcode:
program Project1;

{$AppType Console}

uses
  SysUtils;

type
  TGenericClassA = class
    public
    procedure Method1; virtual;
  end;
  TGenericClassB = class(TGenericClassA)
    public
    procedure Method1; override;
  end;
  { TClassA = TGenericClassA<TObject>; ist etwa }
  TClassA1 = TGenericClassA;
  { und nicht }
  TClassA2 = class(TGenericClassA);

  TClassB = TGenericClassB;

{ TGenericClassA }

procedure TGenericClassA.Method1;
begin
  WriteLn('A: TGenericClassA.Method1 aufgerufen');
end;

{ TGenericClassB }

procedure TGenericClassB.Method1;
begin
  WriteLn('B: TGenericClassB.Method1 aufgerufen');
end;

procedure Test;
var
  B: TClassB;
begin
  B:=TClassB.Create;
  try
    { 1 }
    TClassA1(B).Method1;
    (B as TClassA1).Method1;

    { 2 }
    TClassA2(B).Method1;
    (B as TClassA2).Method1;
    { [Pascal Fehler] Project1.dpr(50): E2010 Inkompatible
    Typen: 'TClassA2' und 'TGenericClassB' }
  finally
    B.Free;
  end;
end;

begin
  Test;
  ReadLn;
end.
Ich hatte mir die Vorfahrlinien irgendwie so gedacht:
TGenericClassA<T> >> TGenericClassB<T>
TGenericClassA<TObject> >> TGenericClassB<TObject> >> TClassB
TGenericClassA<TObject> >> TClassA

TClassA wäre also nicht in der Linie von TClassB.

Tatsächlich sind die Linien so:
TGenericClassA<T> >> TGenericClassB<T>
TGenericClassA<TObject> >> TGenericClassB<TObject> = TClassB {2}
TGenericClassA<TObject> = TClassA {2}

Aus 1 und 2 ließe sich also formulieren:
TClassA >> TGenericClassB<TObject> = TClassB

Damit liegt TClassA in der Vorfahrlinie und die Typumwandlung "B as TClassA" ist keine Überraschung mehr. -- Vielen Dank!

Ich glaub' ich muss mir unbedingt ne Möglichkeit verschaffen mit Generika selbst ein bisschen herumzuspielen...

@I.A: Ja, allerdings, ich habe keine Möglichkeit, Generika einmal testweise auszuprobieren. Deshalb habe ich jemanden gefragt. Himitsu war so freundlich.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:54 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 by Thomas Breitkreuz