Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Interfaces UND Objektreferenzen mischen (https://www.delphipraxis.net/164278-interfaces-und-objektreferenzen-mischen.html)

himitsu 6. Nov 2011 08:25

Interfaces UND Objektreferenzen mischen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Im Prinzip benötige ich sowas für eine Art Framework, wo man eigene Objekte, eigene Interfaces und fremde Interfaces (gekpselt in eigenen Objekten) paralell nutzen kann.
Also wo ein objekt gleichzeitig als Objektreferenz existiert und als Interface.

Leider ging es nicht "einfach", da http://www.delphipraxis.net/164271-d...ml#post1134663 nicht möglich ist.
Und leider bekommt man im constructor nicht mit, ob das erzeugte Objekt, danach als Objekt oder als Interface genutzt wird. (das zusätzliche CreateIntf erspart)


Delphi-Quellcode:
type
  _RefObject = class abstract(TObject)
  private
    FRefCount: Integer;
    FVCLFreed: Boolean;
    FNoDestroy: Integer;
    class function _NewInstance: TObject; virtual; abstract;
    procedure _FreeInstance;     virtual; abstract;
    procedure _AfterConstruction; virtual; abstract;
    procedure _BeforeDestruction; virtual; abstract;
    destructor _Destroy;         virtual; abstract;
  public
    class function NewInstance: TObject; override;
    procedure FreeInstance;     override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    destructor Destroy;         override;
  end;
  TRefObject = class(_RefObject, IInterface)
    class function _NewInstance: TObject; override;
    procedure _FreeInstance;     override;
    procedure _AfterConstruction; override;
    procedure _BeforeDestruction; override;
    destructor _Destroy;         override;
  public
    class function NewInstance: TObject; reintroduce; virtual;
    procedure FreeInstance;     reintroduce; virtual;
    procedure AfterConstruction; reintroduce; virtual;
    procedure BeforeDestruction; reintroduce; virtual;
    constructor Create;         overload;   virtual;
    constructor CreateIntf;     overload;
    class function CreateIntf<I: IInterface>: I; overload;
    destructor Destroy;        reintroduce; virtual;
    { IInterface }
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; virtual; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;
Es wird also nun jeweils erst bei dem wirklich freigegeben, welches zurletz noch vorhanden ist.
Womit dann z.B. Sowas gemacht werden könnte:
Delphi-Quellcode:
var
  i: IInterface;
  o: TRefObject;
begin

o := TRefObject.Create;
o.Free;

i := TRefObject.CreateInf;
i := nil;

o := TRefObject.Create;
i := o as IInterface;
i := nil;
o.Free;

o := TRefObject.Create;
i := o as IInterface;
o.Free;
i := nil;

TRefObject.Create.Free;
Das Ganze ist also quasi eine Mischung aus einem Interface-Pattern und einem Singleinstanz-Pattern.

Wäre schön, wenn da nochmal wer reinsehn könnte ... nicht daß ich da noch irgendwas übersehn hab. :shock:

Wenn jemand an der Referenzzählung (_AddRef und _Release) rumspielt oder wenn wer Free/Destroy doppelt aufruft, dann knallt es natürlich irgendwo, aber sowas passiert ja auch bei den normalten Objekten und Interfacen, weswegen ich da keinen Anlaß sah, Dieses (zumindestens das Free/Destroy) entsprechend zu behandeln ... zu oft _Release wird aber erkannt.

Stevie 6. Nov 2011 11:40

AW: Interfaces UND Objektreferenzen mischen
 
Die Unterscheidung zwischen Create und CreateIntf ist etwas unglücklich gewählt, hier verhält sich nämlich das direkte zuweisen auf ein Interface mit Create nicht so, wie man es gewohnt ist. Daher lieber die neue Funktionalität explizit benamen.
Die Methode
Delphi-Quellcode:
CreateIntf<T>
ist so nicht korrekt und bringt einen E2010: Incompatible types, wenn man irgendwas anders als IInterface als T angibt. Dafür müsstest du Supports oder GetInterface benutzen.

Aber nebenbei bemerkt, frag ich mich, warum du solche Verrenkungen für was total Simples machst...

Delphi-Quellcode:
type
  TRefObject = class(TObject, IInterface)
  private
    FKeepAlive: Boolean;
    FRefCount: Integer;
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    constructor Create(KeepAlive: Boolean = False);
    procedure FreeInstance; override;
    procedure AfterConstruction; override;
    class function NewInstance: TObject; override;
    property RefCount: Integer read FRefCount;
  end;

{ TRefObject }

procedure TRefObject.AfterConstruction;
begin
  InterlockedDecrement(FRefCount);
end;

constructor TRefObject.Create(KeepAlive: Boolean);
begin
  FKeepAlive := KeepAlive;
end;

procedure TRefObject.FreeInstance;
begin
  if (FRefCount = 0) or not FKeepAlive then
    inherited;
  FKeepAlive := False;
end;

class function TRefObject.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  TRefObject(Result).FRefCount := 1;
end;

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

function TRefObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TRefObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if (Result = 0) and not FKeepAlive then
    Destroy;
end;
getestet mit:

Delphi-Quellcode:
var
  i: IInterface;
  o: TRefObject;
begin
  o := TRefObject.Create;
  o.Free;

  i := TRefObject.Create;
  i := nil;

  o := TRefObject.Create(True);
  i := o;
  i := nil;
  o.Free;

  o := TRefObject.Create(True);
  i := o;
  o.Free;
  i := nil;

  TRefObject.Create.Free;

  i := TRefObject.Create;
end;

himitsu 6. Nov 2011 12:07

AW: Interfaces UND Objektreferenzen mischen
 
Jetzt leite mal von deiner Klasse ab und überschreib z.B. den Destructor,
Das kommt ja häufig vor, daß man sich einen eigenen Destructor und Constructor erstellt und darin irgendwas erzeugt/freigibt.

Du mußt in allen Nachfahren höllisch aufpassen, da die überschriebenen Methoden immer Vorrang haben ... darum auch die Verrenkungen. :angle:

PS: Genau so hatte ich auch angefangen. :lol:

Stevie 6. Nov 2011 13:43

AW: Interfaces UND Objektreferenzen mischen
 
Zitat:

Zitat von himitsu (Beitrag 1134690)
Jetzt leite mal von deiner Klasse ab und überschreib z.B. den Destructor,
Das kommt ja häufig vor, daß man sich einen eigenen Destructor und Constructor erstellt und darin irgendwas erzeugt/freigibt.

Dann schreibt man dort inherited rein? Und NewInstance und FreeInstance überschreibt man mal nicht so aus "Versehen".

himitsu 6. Nov 2011 14:11

AW: Interfaces UND Objektreferenzen mischen
 
Ein Inherited alleine hilft nicht, denn der Destructor darf nicht ausgeführt werden, wenn das Objekt eigentlich noch nicht weg darf, weil noch Interfacereferenzen existieren, obwohl Free aufgerufen wurde.

Du müßtest also in allen Nachfahren und bei allen Destructoren, sowie den eventuellen FreeInstance und BeforeDestruction neben dem inherited auch die IF-Abfrage mit reinmachen, welche die Freigabe verhindern.

Stevie 6. Nov 2011 15:04

AW: Interfaces UND Objektreferenzen mischen
 
Zitat:

Zitat von himitsu (Beitrag 1134701)
Ein Inherited alleine hilft nicht, denn der Destructor darf nicht ausgeführt werden, wenn das Objekt eigentlich noch nicht weg darf, weil noch Interfacereferenzen existieren, obwohl Free aufgerufen wurde.

Crap :wall: Hab garnicht mehr dran gedacht, dass Free stumpf Destroy aufruft :oops:


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