Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Zuweisung eines Methodenzeigers bei vererbten Interfaces (https://www.delphipraxis.net/192477-zuweisung-eines-methodenzeigers-bei-vererbten-interfaces.html)

dpg123 21. Apr 2017 13:03

Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Hallo liebes Forum,

entschuldigt den etwas kryptischen Titel. Konnte die Frage iwie nicht prägnanter in einem Satz zusammenfassen...

Ich habe folgendes Design, welches ich gerne benutzen würde:

Delphi-Quellcode:
  IInterfA = interface(IInterface)
  ['{47B0ED79-41C2-4F2F-BA29-521D6B9872FB}']

    function exec1(): string;

  end;

  IInterfB = interface(IInterfA)
  ['{47B0ED79-41C2-4F2F-BA29-521D6B9872FB}']

    function exec2(): string;

  end;


  TImplB = class(TInterfacedObject,IInterfB)

    function exec1(): string;

    function exec2(): string;

  end;


  MInterfA = function(): IInterfA of object;

  MInterfB = function(): IInterfB of object;


  TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

  public

    getInterfA: MInterfA;

  published

    function factoryB(): IInterfB;

  end;
Aufgabe, Frage, Diskussion ist jetzt die Zuweisung der Function factoryB() zur Methodenzeiger-Variable getInterfA.

Eine Möglichkeit für die Zuweisung habe ich gefunden:

Delphi-Quellcode:

procedure TForm1.FormCreate(Sender: TObject);
var
  lB: MInterfB;

begin

  lB := self.factoryB;

  Self.getInterfA := MInterfA( lB );

  ShowMessage(self.getInterfA.exec1());

end;

function TForm1.factoryB: IInterfB;
begin
  result := TImplB.create();
end;

{ TImplB }

function TImplB.exec1: string;
begin
  Result := 'Hello 1';
end;

function TImplB.exec2: string;
begin
  Result := 'Hello 2';
end;
Geht die Zuweisung auch direkt, ohne die Variable lB und damit auch ohne Anlegen des Typs MInterfB?

Das showmessage hat gemacht was es soll, aber tappe ich mit dem Design in iwelche Fallen? Gibt es Situation, in denen dieser Downcast Probleme macht? Ist es überhaupt gutes Design? Wie könnte man es eleganter lösen?

Dank und Gruß

Olli73 21. Apr 2017 13:39

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Warum nicht einfach

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  lB: MInterfB;
begin
  ShowMessage(factoryB.exec1);
  (*
  lB := self.factoryB;
  Self.getInterfA := MInterfA( lB );
  ShowMessage(self.getInterfA.exec1());
  *);
end;
Oder habe ich dein Problem falsch verstanden?

dpg123 21. Apr 2017 13:46

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Zitat:

Zitat von Olli73 (Beitrag 1368576)
Warum nicht einfach

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  lB: MInterfB;
begin
  ShowMessage(factoryB.exec1);
  (*
  lB := self.factoryB;
  Self.getInterfA := MInterfA( lB );
  ShowMessage(self.getInterfA.exec1());
  *);
end;
Oder habe ich dein Problem falsch verstanden?

Das Ziel ist factoryB über den Datentyp MInterfA mit der Variable getInterfA an andere Programmteile zu übergeben. Der vorgestellte Code war nur ein Minimalbsp um das Problem zu veranschaulichen.

stahli 21. Apr 2017 13:48

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Verstehe ich richtig, dass Du prüfen willst, ob Dein Objekt nicht nur IInterfB sondern auch IInterfA unterstützt?

Dann ginge das vielleicht so:

Delphi-Quellcode:
TImplB = class(TInterfacedObject, IInterfB, IInterfA)

...
var
  lA: IInterfA;

if Supports(lB, IInterfB, lA) then
  DoSomething(lA);
Das geht mit jedem Interface, unabhängig davon, ob es sich um Ableitungen handelt oder nicht.

dpg123 21. Apr 2017 14:00

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Zitat:

Zitat von stahli (Beitrag 1368579)
Verstehe ich richtig, dass Du prüfen willst, ob Dein Objekt nicht nur IInterfB sondern auch IInterfA unterstützt?

Dann ginge das vielleicht so:

Delphi-Quellcode:
TImplB = class(TInterfacedObject, IInterfB, IInterfA)

...
var
  lA: IInterfA;

if Supports(lB, IInterfB, lA) then
  DoSomething(lA);
Das geht mit jedem Interface, unabhängig davon, ob es sich um Ableitungen handelt oder nicht.

Die Signatur von
Delphi-Quellcode:
MInterfA = function(): IInterfA of object;
unterscheidet sich von
Delphi-Quellcode:
function factoryB(): IInterfB;
ja nur durch einen "Downcast". Meiner Meinung eine gültige Zuweisung, bzw. ein legitimes Design.

Der Compiler lässt mich keine direkte Zuweisung machen:
Delphi-Quellcode:
Self.getInterfA := self.factoryB;
Dafür könnte es ja Gründe geben, die ich nicht verstehe/kenne, aber gerne wissen würde. Daher meine Fragen nach der einfachsten Art der Zuweisung und ob es Argumente gibt, dies nicht zu tun, bzw was dabei zu beachten ist.

stahli 21. Apr 2017 14:12

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Zitat:

Zitat von dpg123 (Beitrag 1368580)
Der Compiler lässt mich keine direkte Zuweisung machen:
Delphi-Quellcode:
Self.getInterfA := self.factoryB;

Versuche mal nur diese Änderung:
Delphi-Quellcode:
TImplB = class(TInterfacedObject, IInterfB, IInterfA)

IInterfA wird nicht automatisch unterstützt, nur weil IInterfB davon abgeleitet ist. Du musst dieses also explizit zusätzlich angeben.
Dann solltest Du auf Deine Methoden (die ich nicht wirklich verstehe, zumal die Implementierungen im Beispiel fehlen) verzichten können.

Olli73 21. Apr 2017 14:14

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Nur zum Verständnis: Aus dem Feld "getInterfA: MInterfA;" was sich wie eine Methode anhört (wegen dem get), eine echte Methode zu machen, ist in deinem Fall keine Lösung?

Der schöne Günther 21. Apr 2017 14:21

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Im Endeffekt geht es dir doch um Kovarianz für Rückgabewerte, oder? Interfaces an sich haben damit eigentlich gar nichts zu tun. Du willst dass ein Methodenzeiger auf eine "Formenfabrik"-Methode auf eine "Kreisfabrik"-Methode oder eine "Trapezfabrik"-Methode beinhalten kann.

Vereinfachtes Beispiel:
Delphi-Quellcode:
program Project19;

uses System.SysUtils;

type
   TBase = class(TObject);
   TSub = class(TBase);

function createBase(): TBase;
begin
   Result := TBase.Create();
end;

function createSub(): TSub;
begin
   Result := TSub.Create();
end;

var
   baseFactory: TFunc<TBase>;
   subFactory: TFunc<TSub>;
begin
   baseFactory := createBase;
   //baseFactory := createSub; // E2010, kann Delphi nicht

   subFactory := createSub;
   //subFactory := createBase; // E2010, war zu erwarten
end.

Das geht in Delphi nicht, Delphi kann keine Kovarianz für Rückgabetypen. Ebenfalls leider nicht für z.B. Var-parameter.

Der erste QC-Eintrag sogar schon aus 2005 ;-)

http://qc.embarcadero.com/wc/qcmain.aspx?d=16089



Sprachen wie Java z.B. können es:
https://blogs.oracle.com/sundararaja..._types_in_java

dpg123 21. Apr 2017 14:58

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Zitat:

Zitat von Olli73 (Beitrag 1368583)
Nur zum Verständnis: Aus dem Feld "getInterfA: MInterfA;" was sich wie eine Methode anhört (wegen dem get), eine echte Methode zu machen, ist in deinem Fall keine Lösung?

Ja, der Name verwirrt wirklich, sorry.

Hm, könnte evt ein Workaround sein... In der Praxis ist die Variable getInterfA: MInterfA; nicht im selben Objekt wie die function factoryB(): IInterfB; dh ich brauche schon mal ieinen Datentyp um factoryB() an andere Teile zu übergeben, aber man könnte natürlich eine Methode in Form1 deklarieren, die den Cast macht:

Delphi-Quellcode:
TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

  public

    getInterfA: MInterfA;

  published

    function factoryACast(): IInterfA;

    function factoryB(): IInterfB;

  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin

  Self.getInterfA := Self.factoryACast;

  ShowMessage(self.getInterfA.exec1());

end;

function TForm1.factoryACast: IInterfA;
begin
  Result := self.factoryB();
end;

function TForm1.factoryB: IInterfB;
begin
  result := TImplB.create();
end;
damit könnte ich mir den Typ MInterfB schon mal sparen... Könnte schöner/sauberer als meine Lösung oben sein !?

Trotzdem würden mich obigen Fragen weiterhin interessieren ...

Zitat:

Im Endeffekt geht es dir doch um Kovarianz für Rückgabewerte, oder? Interfaces an sich haben damit eigentlich gar nichts zu tun. Du willst dass ein Methodenzeiger auf eine "Formenfabrik"-Methode auf eine "Kreisfabrik"-Methode oder eine "Trapezfabrik"-Methode beinhalten kann.
Denke ja... muss dazu sagen, dass ich nur Delphi 2006 und keine Generics zur Verfügung habe... daher hab ich versucht zu tricksen (s.o.)... mit welchen Konsequenzen ist mir weiterhin unklar...

Olli73 21. Apr 2017 15:11

AW: Zuweisung eines Methodenzeigers bei vererbten Interfaces
 
Zitat:

Zitat von dpg123 (Beitrag 1368588)
Denke ja... muss dazu sagen, dass ich nur Delphi 2006 und keine Generics zur Verfügung habe... daher hab ich versucht zu tricksen (s.o.)... mit welchen Konsequenzen ist mir weiterhin unklar...

Wenn ich Günther richtig verstanden habe, liegt es nur an Delphi: Es merkt nicht, dass dein "Downcast" beim Rückgabetyp eigentlich ohne Probleme zulässig wäre und bringt stattdessen eine Fehlermeldung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:37 Uhr.
Seite 1 von 2  1 2   

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