Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Interfaces und Vererbung? (https://www.delphipraxis.net/142087-interfaces-und-vererbung.html)

Whookie 21. Okt 2009 22:42


Interfaces und Vererbung?
 
Hallo Delphianer!
Ich hab mal einwenig mit Interfaces herumgespielt und aus Quellen hier im Forum eine kleine Schnittstellen-DLL zusammengebaut. Leider kriege ich eine Zugriffsverletzung und bin mir nicht im klaren darüber was der Grund dafür ist.

Folgender Code befindet sich in der DLL:
Delphi-Quellcode:
library IntfDll;

uses
  uBasic in 'uBasic.pas';

{$R *.res}

Type
  TBasic = Class(TInterfacedObject, IBasic)
  private
    fValue: Integer;
  public
    constructor Create; virtual;
    function Basic: Integer; stdcall;
  end;

  TBasic2 = Class(TBasic, IBasic2)
  private
    fValue2: Integer;
  public
    constructor Create; override;
    function Basic2: Integer; stdcall;
  end;



function GetIntf(Id: Integer): IBasic; stdcall;
begin
  Case Id Of
    0: Result := TBasic.Create;
    1: Result := TBasic2.Create;
    Else Result := NIL;
  End;
end;



constructor TBasic.Create;
begin
  inherited;
  fValue := 1;
end;

function TBasic.Basic: Integer;
begin
  Result := fValue;
end;




constructor TBasic2.Create;
begin
  inherited;
  fValue2 := 2;
end;

function TBasic2.Basic2: Integer;
begin
  Result := fValue + fValue2;
end;



exports
  GetIntf;
 
begin
end.
Dazu gibts eine kleine Schnittstellen-Unit die ich auch im Hauptprojekt verwende:

Delphi-Quellcode:
unit uBasic;

interface

Type
  IBasic = Interface
    ['{BF08333B-DBEE-4108-9466-6ECEE4AFD9A1}']
    function Basic: Integer; stdcall;
  end;

  IBasic2 = Interface(IBasic)
    ['{38E3D185-5337-4C5B-A3C8-FC7BBC82B855}']
    function Basic2: Integer; stdcall;
  end;


function GetIntf(Id: Integer): IBasic; stdcall;


implementation

function GetIntf(Id: Integer): IBasic; external 'IntfDll.dll' name 'GetIntf';

end.
Und zum Abschluss das Testprogramm dazu:

Delphi-Quellcode:
program Tester;

{$APPTYPE CONSOLE}

uses
  SysUtils, windows,
  uBasic in 'uBasic.pas';

Var
  B: IBasic;
  B2: IBasic2;
begin
  WriteLn;
  B := GetIntf(0); //
  WriteLn('Basic.Basic  = ',B.Basic);
  B := NIL;

  WriteLn;
  B2 := IBasic2(GetIntf(1));
  WriteLn('Basic2.Basic = ', B2.Basic);
  WriteLn('Basic2.Basic2 = ', B2.Basic2);     //<-crash
  B2 := NIL;
end.

Ich will in der Art von TBasic noch weiter Klassen ableiten (mit zugehörigem Interface) aber auch von TBasic2 und GetIntf soll mir dann das Interface liefern... Das ganze sollte im übrigen auch von C# aus ansprechbar sein (muss ich da auf was besonderes achten?)...

Ich wäre für jeden Hinweis dankbar, bzw. mach ich ja was grundlegend falsch?

/edit: inherited zu TBasic.Create hinzugefügt

LG

himitsu 21. Okt 2009 23:04

Re: Interfaces und Vererbung?
 
bezüglich C#:

die Aufrufkonventionen (stdcall) und die verwendeten Typen (Integer) sollten da keine Probleme machen.
> keine Objekte, dynamische Arrays, Strings (String, AnsiString, UnicodeString) verwenden ... möglichst also nichts delphispezifisches (WideString geht aber, da es intern ein C-Typ ist)



ansonsten konnte ich jetzt keinen Fehler entdecken, außer daß in TBasic.Create das inherited fehlt.

Basilikum 22. Okt 2009 06:48

Re: Interfaces und Vererbung?
 
ersetze mal das
Delphi-Quellcode:
B2 := IBasic2(GetIntf(1));
durch
Delphi-Quellcode:
B2 := GetIntf(1) as IBasic2;

Stevie 22. Okt 2009 06:51

Re: Interfaces und Vererbung?
 
Der hardcast eines interfaces bewirkt ein interface copy und keinen interface cast. Der korrekte Weg wäre:
Delphi-Quellcode:
B2 := GetIntf(1) as IBasic2;
wenn du sicher bist, dass dein angefordertes Interface IBasic auch IBasic2 unterstützt, ansonsten bist du mit
Delphi-Quellcode:
if Supports(GetIntf(1), IBasic2, B2) then
auf der sicheren Seite.

Whookie 22. Okt 2009 08:39

Re: Interfaces und Vererbung?
 
Zitat:

Zitat von himitsu
bezüglich C#:
(WideString geht aber, da es intern ein C-Typ ist)

WideString hab ich auch in der Planung, ich bin momentan noch bei Delphi 2007. Aber wie schaut das dann mit Delphi 2009 aus da ist WideString doch nativ? Gibts dann eine Alternative?

LG

Apollonius 22. Okt 2009 12:44

Re: Interfaces und Vererbung?
 
Achtung! In Delphi 2009 gibt es zusätzlich den Typen UnicodeString, aber du musst weiterhin WideString verwenden. UnicodeString wird nämlich von Delphi verwaltet, WideString aber von COM. Nur mit WideString kann C# direkt umgehen.

himitsu 22. Okt 2009 12:59

Re: Interfaces und Vererbung?
 
Jupp, der UnicodeString ist quasi die Erweiterung vom AnsiString
- läuft über den Delphi-Speichermanager
- verfügt über 'ne Referenzzäählung
- ist praktisch auch wie ein dynamisches Array aufgebaut
- also vorwiegend delphieigenes Zeugs

der WideString ist dagegen eine Kapselung einiger Befehle der oleaut32.dll,
welche man also auch in C zur Verfügung hat
MSDN-Library durchsuchenSysAllocStringLen, MSDN-Library durchsuchenSysReAllocStringLen, MSDN-Library durchsuchenSysFreeString und MSDN-Library durchsuchenSysStringLen

himitsu 24. Mär 2011 23:27

AW: Interfaces und Vererbung?
 
Zitat:

Zitat von grade in der Shoutbox
NamenLozer, 22:19
@himitsu, 20:41: ich weiß, hab ich ja auch geschrieben. Ich versteh nur nicht, wieso das gleiche nicht bei Interfaces geht.

himitsu, 20:51
Und da Interfacemethoden standardmäßig über einen Index verwaltet werden, kann es da eh keine Mehrfachvererbung geben.

himitsu, 20:41
Du kannst aber mehrere Interfaces einem Objekt zuweisen.

Stevie, 19:53

Weil "keine Mehrfachvererbung" auch für Interfaces gilt? :p

NamenLozer, 14:37
Wieso kann eine Klasse mehrere Interfaces implementiren, aber ein Interface nicht von mehreren Interfaces abgeleitet sein?

Warum wird das wohl so ein :?:

Normaler Weise werden die Methoden bei Interfaces über eine Art "einfache Liste mit Methodenzeigern" verwaltet.
Und bei der Interface-Vererbung werden einfach vom neuen Interface die Methoden hinten drangehängt.
Und genau deswegen kann/darf man nicht einfach Methoden weglassen oder in anderer Reihenvolge deklarieren, wenn man "Kopieen" eines Interfaces erstellt.

Delphi-Quellcode:
type
  IMyIntfA = interface
    procedure Aaa; {0}
    procedure Bbb; {1}
    procedure Cee; {2}
    procedure Ddd; {3}
    procedure Eee; {4}
    procedure Fff; {5}
    procedure Ggg; {6}
  end;

  IMyIntfB = interface
    procedure Aaa; {0}
    procedure Bbb; {1}
    procedure Cee; {2}
    procedure Eee; {3} // vertauscht
    procedure Ddd; {4} //
    procedure Fff; {5}
    // hier fehlt was
  end;

  IMyIntfC = interface
    procedure Hhh; {0}
    procedure Iii; {1}
    procedure Jjj; {2}
  end;

  IMyIntfD = interface(IMyIntfA)
    procedure Hhh; {7}
    procedure Iii; {8}
    procedure Jjj; {9}
  end;

  IMyIntfX = interface(IMyIntfA, IMyIntfC)
    // implizit über IMyIntfA enthalten
    //procedure Aaa; {0}
    //procedure Bbb; {1}
    //procedure Cee; {2}
    //procedure Ddd; {3}
    //procedure Eee; {4}
    //procedure Fff; {5}
    //procedure Ggg; {6}

    // implizit über IMyIntfC enthalten
    //procedure Hhh; {0}
    //procedure Iii; {1}
    //procedure Jjj; {2}

    // neue Methoden von IMyIntfX
    procedure Kkk; {7}
    procedure Lll; {8}
    procedure Mmm; {9}
  end;
  // aber jetzt gäbe es mehrfach gleiche Indize,
  // welches sich nicht adressieren ließen,
  // denn was wäre denn nun z.B. eine 1?

  // ok, also dann könnte Delphi theoretisch die Nummern "einfach" weiterzählen,
  IMyIntfY = interface(IMyIntfA, IMyIntfC)
    // implizit über IMyIntfA enthalten
    //procedure Aaa; {0}
    //procedure Bbb; {1}
    //procedure Cee; {2}
    //procedure Ddd; {3}
    //procedure Eee; {4}
    //procedure Fff; {5}
    //procedure Ggg; {6}

    // implizit über IMyIntfC enthalten
    //procedure Hhh; {7}
    //procedure Iii; {8}
    //procedure Jjj; {9}

    // neue Methoden von IMyIntfY
    procedure Kkk; {10}
    procedure Lll; {11}
    procedure Mmm; {12}
  end;
  // aber NEIN, dann würden die Indize nicht mehr mit denen vom IMyIntfC übereinstimmen

IMyIntfA = das Ausgangsinterface
IMyIntfB = enthält eigentlich einige Methoden von IMyIntfA, außer daß etwas vertauscht ist.
Wenn man dieses also auf ein Interface vom Typ IMyIntfA anwendet, dann klappt daß, außer daß z.B. beim Aufruf von Eee intern das Ddd ausgeführt wird.
IMyIntfC und IMyIntfD = die sehen eigentlich gleich aus, sind es aber nicht
IMyIntfX und IMyIntfY = dieses geht zum Glück nicht, denn wie man sieht, stimmt da nix mehr.

Namenloser 25. Mär 2011 15:08

AW: Interfaces und Vererbung?
 
Also nochmal... nachdem ich jetzt an diesem Schullaptop versehentlich 2 mal den Tab geschlossen habe und den Text nicht mehr wieder herstellen konnte, versuche ich es ein weiteres mal.

Ich habe deine Erklärung jetzt glaube ich halbwegs verstanden, aber was ich nicht verstehe: Wieso kann man dann in einer Klasse mehrere voneinander unabhängige Interfaces implementieren?

Delphi-Quellcode:
type
  IIntfA = interface
    procedure ProcA; {0}
  end;

  IIntfB = interface
    procedure ProcB; {0}
  end;

  TMyClass = class(IIntfA, IIntfB)
    procedure ProcA;
    procedure ProcB;
  end;

mkinzler 25. Mär 2011 15:10

AW: Interfaces und Vererbung?
 
Das ist doch gerade der Sinn von Interfaces.

Namenloser 25. Mär 2011 15:44

AW: Interfaces und Vererbung?
 
Natürlich, aber das erklärt nicht, warum es funktioniert.

himitsu 25. Mär 2011 18:16

AW: Interfaces und Vererbung?
 
In den Interfaces wird eine Linkliste zu den Methoden angelegt.
Jedes Interface hat seine eigene Liste.
Bei dir steht nun im Interface IIntfA an der Stelle [0] ein Verweis zu ProcA des internen Objektes
und in IIntfB steht an der Stelle [0] ein Verweis zu ProcB des internen Objektes.
Die Listen stehen in den Interfaces, also ist es dem Objekt vollkommen egal, bzw. es bekommt nicht mit, ob die verlinkten Interfaces an Stelle [0] Unterschiedliche Methodenzeiger enthalten.

PS: deshalb kann man Interfaces auch nicht einfach so casten, denn dann würden ja die Adressen des falschen Interfaces verwendet.

Namenloser 25. Mär 2011 18:25

AW: Interfaces und Vererbung?
 
Ähh ja, hast recht, hatte einen Denkfehler.
Delphi-Quellcode:
var
  IntfA: IIntfA;
  IntfB: IIntfB;
begin
  IntfA := TMyClass.Create;
  IntfB := TMyClass.Create;
Hierbei werden ja zwei komplett andere, inkompatible Interfaces erzeugt, die sich deshalb auch nicht casten lassen.

Trotzdem schade, dass die Mehrfachvererbung bei Interfaces so nicht geht. Ist irgendwie unintuitiv. Ich bin sicher, man hätte das technisch auch anders lösen können... aber nun denn... muss wohl damit leben.

Stevie 25. Mär 2011 20:49

AW: Interfaces und Vererbung?
 
Ich versteh nicht, was alle so an Mehrfachvererbung haben. Vererbung in allen Ehren, aber an sich eine hohe Kopplung. Hohe Kopplung ist oft nichts gutes. Und eine Mehrfachvererbung macht das noch viel schlimmer. Und schmunzeln muss ich dann noch, wenn ich die zahlreichen Beispiele für diese sehe.

Namenloser 25. Mär 2011 20:52

AW: Interfaces und Vererbung?
 
Bei Klassen gebe ich dir recht, bei Interfaces ist es imo etwas anderes.

Stevie 25. Mär 2011 20:53

AW: Interfaces und Vererbung?
 
Zitat:

Zitat von NamenLozer (Beitrag 1091063)
Bei Klassen gebe ich dir recht, bei Interfaces ist es imo etwas anderes.

Klar, bei Interfaces könnte man sich noch vorstellen, dass man 2 verschiedene Interfaces hat, die auch in Kombination vorkommen und man dann natürlich dieses als 1 Interface haben möchte. Läuft aber in meinen Augen dem Single responsibility principle zuwider.

Und selbst wenn, worin läge der Vorteil eines IWalkAndFly Interfaces, was von IWalk und IFly ableitet, wenn ich in meiner Klasse sowohl IWalk als auch IFly implementieren kann und auch sogar die Möglichkeit habe eine IWalk Referenz zu fragen, ob sie auch nen IFly supportet?


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:57 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