Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Implementierung eines Interfaces (https://www.delphipraxis.net/71615-implementierung-eines-interfaces.html)

3_of_8 18. Jun 2006 14:55


Implementierung eines Interfaces
 
Morgen.

Ich hab mir grad ein Interface definiert und das dann implementiert.

Der Compiler verlangt nun von mir, dass ich auch die Methoden QueryInterface, _AddRef und _Release implementiere.

Was sind das für Methoden, wie soll ich die implementieren?

mkinzler 18. Jun 2006 15:00

Re: Implementierung eines Interfaces
 
Das sind eigentlich Methoden von TObject. Zeig mal ein bischen Code.

Dax 18. Jun 2006 15:02

Re: Implementierung eines Interfaces
 
QueryInterface benutzt der Compiler bei as-Casts zu Interfaces um zu kucken, ob der zu castende Typ überhaupt dieses Interface implementiert. _AddRef und _Release sind zur Referenzzählung da, wenn die Referenzen auf das Interface auf 0 fallen, gibt es sich automatisch frei: GC für faule -> ausschalten wenn möglich.

Ganz einfach gehts so:
Delphi-Quellcode:
type
  TMyInterfacedClass = class(TInterfacedObject, IMyInterface)
    ...
  end;
Wenn ich in Delphi mit Interfaces arbeite, benutze ich immer das als Basisklasse:
Delphi-Quellcode:
type
  IHAMInterface = interface
    ['{F7509748-217A-42EB-B6FB-A45C25310F8A}']    
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;

    procedure AllowDestruction;
  end;

  THAMObject = class(TObject, IHAMInterface)
  private
    fDestroying: Boolean;
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; virtual; stdcall;
    function _Release: Integer; virtual; stdcall;

    procedure AllowDestruction;
  public
    constructor Create; virtual;

    property Destroying: Boolean read fDestroying write fDestroying;
  end;

procedure THAMObject.AllowDestruction;
begin
  fDestroying := True;
end;

constructor THAMObject.Create;
begin
  inherited;
end;

function THAMObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function THAMObject._AddRef: Integer;
begin
  Result := 1;
end;

function THAMObject._Release: Integer;
begin
  Result := 1;
  if fDestroying then
    Free;
end;

@mkinzler: Eigentlich sinds doch Methoden von TInterfacedObject?

mkinzler 18. Jun 2006 15:04

Re: Implementierung eines Interfaces
 
Zitat:

@mkinzler: Eigentlich sinds doch Methoden von TInterfacedObject?
Stimmt!

3_of_8 18. Jun 2006 15:33

Re: Implementierung eines Interfaces
 
['{F7509748-217A-42EB-B6FB-A45C25310F8A}']

Was ist das? Hat das was mit Serialisierung zu tun? Also mein Code ist bisher so:

Delphi-Quellcode:
type
  IMap=interface
    function Get(Key: Variant): TObject;
    procedure Put(Key: Variant; Data: TObject);
    procedure Insert(Key: Variant; Data: TObject);
    procedure Delete(Key: Variant);
    property Data[Key: Variant]: TObject read Get write Put; default;
  end;

  TTreeMap = class(TObject, IMap)
  protected
    FRoot: TTreeMapNode;
  public
    function Get(Key: Variant): TObject;
    procedure Put(Key: Variant; Data: TObject);
    procedure Insert(Key: Variant; Data: TObject);
    procedure Delete(Key: Variant);
    property Data[Key: Variant]: TObject read Get write Put; default;
  end;
EDIT: OK, hab jetzt rausgefunden, wie ichs machen kann. Einfach nicht von TObject ableiten sondern von TInterfacedObject. Danke.

Basilikum 18. Jun 2006 15:58

Re: Implementierung eines Interfaces
 
Zitat:

Zitat von Dax
gibt es sich automatisch frei: GC für faule -> ausschalten wenn möglich.

kannst du zu dieser Aussage eine Erklärung/Begründung geben ?

Dax 18. Jun 2006 16:17

Re: Implementierung eines Interfaces
 
Kann ich:

Referenzzählung bei Interfaces ist ähnlich wie die von Strings. Pro Interface-Variable, der eine Instanz der implementierenden Klasse zugewiesen wird, wird der Referenzzähler dieser Klasseninstanz um 1 erhöht. Rennt die Variable aus dem Scope, wird der Zähler entsprechend um 1 verringert. Sobald der Zähler auf 0 fällt, gibt sich die Klasse automatisch frei - kein Speicherleck entsteht, wenn der ursprüngliche Zeiger auf die Klasseninstanz verloren ist. So weit, so gut.

Aber was passiert hier?
Delphi-Quellcode:
IMyInterface = interface
    function GetPartner: IMyInterface;
  end;

TMyClass = class(TInterfacedObject, IMyInterface)
private
  fPartner: IMyInterface;
  procedure SetPartner(const Value: IMyInterface);
  function GetPartner: IMyInterface;
public
  property Partner: IMyInterface read GetPartner write SetPartner;

...

var
  His, Her: TMyClass;

His := TMyClass.Create();
Her := TMyClass.Create;

His.Partner := Her;
Her.Partner := His;
Die Zuweisung ist korrekt. Beide implementieren IMyInterface, also wirds hier nicht knallen.

Jetzt stell dir vor, His rennt aus dem Scope. His' Referenzzähler dropt auf 1. Bei Her ist es genauso. Wenn beide aus dem Scope rennen, droppen beide Zähler auf 1! Speicherleck! Die Klassen hinter den Interfaces werden nicht mehr freigegeben.

Nimm mal dieses C#-Programm und lass es laufen:
Code:
namespace GCTest
{
   interface IPerson {
      IPerson Partner {get; set;}
   }
   
   class Person : IPerson
   {
      int[] DNA = new int[512];
      
      private IPerson partner;
      public IPerson Partner {
         get { return partner; }
         set { partner = value; }
      };
   }
   
   class MainClass
   {
      public static void Main(string[] args)
      {
         while(true) {
            IPerson he, her;
            he = new Person();
            her = new Person();
            he.Partner = her;
            her.Partner = he;
         }
      }
   }
}
Währenddessen beobachte die Speicherauslastung. Es werden jede Menge Paare erstellt - aber die Speicherauslastung geht (bei mir zumindest) nie über 5MB hinaus. Das ist GarbageCollection. Der GC erkennt automatisch, das keine nutzbare Referenz auf die Instanzen mehr vorhanden ist und killt diese aus dem Speicher. Es gibt zwar noch Referenzen - die partner-Referenzen - aber mit denen kann das Programm nix mehr anfangen.

Probiere das selbe in Delphi, und bald wird dein Rechner mit der swapperei anfangen^^

Conclusio: Wenn schon GC, dann richtig ;)

Elvis 18. Jun 2006 16:22

Re: Implementierung eines Interfaces
 
Zitat:

Zitat von Basilikum
Zitat:

Zitat von Dax
gibt es sich automatisch frei: GC für faule -> ausschalten wenn möglich.

kannst du zu dieser Aussage eine Erklärung/Begründung geben ?

Delphi interfaces sind COM interfaces:
Wenn eine Interface-Instanz aus dem Scope rennt oder eine Referenz entfernt wird, veringert sich ihr Referenzzähler.
Erreicht er 0 wird die Instanz zerstört.
Wenn du nicht von TInterfacedObject erbst, kannst du selbst entscheiden, ob du die Referenzen zählen willst. TComponent zum Beispiel hat die Referenzzählung abgeschalten.

Deshalb reicht das hier:
Delphi-Quellcode:
var
  instance : IMyInterface;
begin
  instance := SomeImplementingClass.Create();
  instance.DoSomething();
end;

Basilikum 18. Jun 2006 18:44

Re: Implementierung eines Interfaces
 
Zitat:

Zitat von Dax
Probiere das selbe in Delphi, und bald wird dein Rechner mit der swapperei anfangen^^

Conclusio: Wenn schon GC, dann richtig ;)

dein Beispiel ist so absolut korrekt - aber meines Erachtens etwas an den Haaren herbeigezogen... wenn man so will, wird man bei vielen Konzepten ein Fall finden, bei dem es versagt - insofern finde ich es etwas unpassend, danach sofort das ganze Konzept zu "ächten"...

ich arbeite sehr oft mit Interfaces (Delphi für Win32) und habe bisher noch nie den Fall angetroffen, bei dem Konzept-bedingt Speicherlecks entstehen... aber ich finde Interfaces eine feine Sache (Kapselung, automatische Destruktion, etc.)...

ich entwickle vorwiegend Server-Dienste, die x Wochen lang am Stück laufen - ich denke, etwaige Memory-Leaks hätten sich unterdessen bemerkbar gemacht... :-)


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