Einzelnen Beitrag anzeigen

peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
672 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: TObject As Interface

  Alt 16. Jul 2019, 13:29
Hi zusammen,

ich muss zugeben, ich weiß gerade nicht so recht, wie ich nach meinem Problem suchen kann,
daher poste ich hier, auch wenn es möglicherweise schon eine Antowrt dazu irgendwo gibt.

Es handelt sich hierbei um eine theoretische Frage, da ich das ganze im Projekt nun anders gelöst habe.
Trotzdem würde ich gerne wissen, was ich falsch mache oder wie man das Problem reell lösen könnte.

Ich habe eine vordefinierte Quelle in welchem ich Daten als "TObject" speichern kann. Ein StringGrid.
In diesem StringGrid speichere ich pro Zeile die Nutzinformationen in StringGrid.Objects[0, iRow].
Zum Speichern verwende ich einen eigenen Typ "TGridObject".
Nun möchte ich eine allgemeingültige Funktion haben, mit welcher ich im StringGrid einen Hint anzeigen kann.
Damit diese Funktion auch auf anderen Grids funktioniert definiere ich ein Interface "IGridInfo",
welches mir die Funktion "GetVal(const iCol : Integer) : String;" bereit stellen soll.
Die Idee ist, dass die "AutoGridHint"-Funktion den String, welcher angezeigt werden kann über diese Funktion erhällt.
Das Objekt muss dann natürlich von diesem Interface erben und die Funktion umsetzen.

Definition vom Interface in der Tools-Unit:
Delphi-Quellcode:
type
  IGridInfo = interface(IInterface)
    ['{675C1022-0376-4583-AF24-8FAA2D008139}']
    Function GetVal(const iCol : Integer) : String;
  end;
Definition des GridObjects in der Unit mit dem Form:
Delphi-Quellcode:
type
  TGridObject = class(TInterfacedObject, IGridInfo)
  private
    FRow : Integer;
    FSomeText : String;
    FSomeMore : String;
  public
    Function GetVal(const iCol : Integer) : String;
    Constructor Create(const iRow : Integer; const sText1 : String; const sText2 : String);
  end;
Im Prinzip funktioniert das, aber Du hast da eine Zeitbombe konstruiert, da von TInterfacedObject abgeleitete Klassen darauf angelegt sind, das die Lebensdauer von Objekten dieser Klasse per Interface reference counting kontrolliert wird. Daher ist es Gift, Objektreferenzen solcher Objekte irgendwo zu speichern.
  1. Du erzeugst eine Objekt der Klasse und speicherst es irgendwo. Der Reference count ist jetzt 0.
  2. Du erzeugst eine Interface-Referenz aus der Objektreferenz, danach ist der reference count 1.
  3. Die Interface-Referenz geht out of scope und dadurch wird automatisch seine _Release-Methode aufgerufen. Der reference count fällt auf 0 und dadurch wird das Objekt zerstört.
  4. Deine gespeicherte Objektreferenz ist nun nicht mehr gültig.

Wenn Du Objekt- und Interface-Referenzen mischen willst brauchst Du eine Basisklasse, die IInterface ohne reference counting implementiert.
Z. B. die hier:

Delphi-Quellcode:
{! == BaseNonRefcountIntfObjU ===========================================
<summary>
This unit provides a base class with a non-reference counted
  implementation of IUnknown/IInterface.</summary>
<author>Dr. Peter Below</author>
<history>
Version 1.0 created 2002-03-28<p>
Version 1.0 created 2009-03-22, changed docs to XML, checked
for Unicode issues.<p>
Version 2.0 created 2016-08-05, renamed to scoped name, adjusted
uses clauses.<p>
Last modified      2016-08-05<p>
</history>
<remarks></remarks>
<copyright>Copyright 2009 by Dr. Peter Below</copyright>
<licence> The code in this unit is released to the public domain without
restrictions for use or redistribution. Just leave the copyright note
above intact. The code carries no warranties whatsoever, use at your
own risk!</licence>
=======================================================================}

unit PB.BaseNonRefcountIntfObjU;
interface

type
  {! <summary>
    Derive classes that need a non-reference counted IInterface
    implementation from this class.</summary>
  }

  TNonRefcountInterfacedObject = class(TObject, IInterface)
  protected
    {! <summary>
      Try to obtain an interface from this object.</summary>
    <returns>S_OK if the interface could be found, E_NOINTERFACE otherwise.</returns>
    <param name="IID">identifies the interface to get, can be a GUID or
      an interface type with a GUID.</param>
    <param name="Obj">receives the found interfaces reference.</param>
    }

    function QueryInterface(const IID: TGUID; out Obj): HResult;
      stdcall;
    {! <summary>
      Does nothing and always returns -1. </summary>
    }

    function _AddRef: Integer; stdcall;
    {! <summary>
      Does nothing and always returns -1. </summary>
    }

    function _Release: Integer; stdcall;
  end;

implementation

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

function TNonRefcountInterfacedObject._AddRef: Integer;
begin
  Result := -1 // -1 indicates no reference counting is taking place
end;

function TNonRefcountInterfacedObject._Release: Integer;
begin
  Result := -1 // -1 indicates no reference counting is taking place
end;

end.
TPersistent ist auch geeignet, diese Klasse hat ebenfalls eine IInterface-Implementierung ohne reference counting, schleppt aber mehr Ballast mit.

Die Supports-Funktion aus System.Sysutils ist die bevorzugte Methode, um aus einer objektreferenz eine Interface-Referenz zu extrahieren. Ein "as" typecast funktioniert normalerweise auch, dafür muß der Interface-Type aber eine GUID haben.
Peter Below
  Mit Zitat antworten Zitat