Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Verständnisfrage: Interface und dazugehöriges Objekt (https://www.delphipraxis.net/169976-verstaendnisfrage-interface-und-dazugehoeriges-objekt.html)

TiGü 23. Aug 2012 15:51

Delphi-Version: 2009

Verständnisfrage: Interface und dazugehöriges Objekt
 
Bin gerade über alten Code gestoßen und bin mir unsicher, ob das so sein darf:
Delphi-Quellcode:
class function TContainer.CreateContainer(...) : IContainer;
var BlaType : IBlaType;
    Example : TContainer; //IContainer;
begin
  Result := nil;
  Example := TContainer.Create(...);
  if Assigned(Example) and FindBlaType(...) then
  begin
    Example.SetIrgendwas(BlaType);
    Result := Example;
  end;
end;
In dem vereinfachten und gekürzten Beispiel muss noch die Variable Example von Typ IContainer sein, so wie im Kommentar angedeutet, oder etwas nicht? :?:
Hab ich hier den Fall vorliegen, vor dem immer gewarnt wird?
"Du sollst nicht Interface- und Objekt-Referenzen mischen!"

s.h.a.r.k 23. Aug 2012 16:09

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
Solltest du wahrlich machen. Sonst bekommst ein Problem mit der autom. Referenzzählung!

Zitat:

Hab ich hier den Fall vorliegen, vor dem immer gewarnt wird?
"Du sollst nicht Interface- und Objekt-Referenzen mischen!"
Jup, hast du! :)

Wobei ich den Code eh nicht ganz verstehe, wenn ich ehrlich bin... Wieso sollte Assigned nach dem Create jemals False liefern? Create ist schon ein Konstruktor? Diese Methode würde in der aktuellen Version eh niemals nil liefern. In so fern Create eine Exception wirft, wird diese ja nicht abgefangen -- außer du hast natürlich diesen Code entfernt ;)

himitsu 23. Aug 2012 16:11

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
In dem gezeigten Code ist es so, daß hier nicht beide Referenzen "gleichzeitig" verwendet werden.
(oftmals macht man sowas, wenn auf eine Funktion des Objekts zugreifen will, welche man "vergessen" hat ins Interface aufzunehmen)

Objekt vor Interface funktioniert oftmals noch.
Delphi-Quellcode:
Example := TContainer.Create(...);
=> Selbst wenn hier Example ein IContainer wäre, würde es kurz vor der Zuweisung noch als Objekt behandelt.

Es kommt jetzt allerdings noch drauf an, was
Delphi-Quellcode:
FindBlaType(...)
und
Delphi-Quellcode:
SetIrgendwas
macht und ob darin eine Interfacereferenz dieses Objekts erzeugt/verwendet wird, denn dann hat man mit diesem Vorgehen ein Problem, da man dann Interface- und Objektreferenz gleichzeitig verwendet.

Objekt nach Interface ist standardmäig nicht möglich, da man hierfür, bei Freigabe der letzen Interfacereferenz, die Freigabeautomatik des Interfaces deaktivieren müßte.
Bzw. man muß an den Stellen, wo man es in als Objekt benötigt, sicherstellen, daß über diese Dauer immer mindestens eine Interfacereferenz existiert.


Außerdem gibt es hier ein Speicherleck, wenn das IF ein False liefert, bzw. wenn es eine Exception gibt ... also wenn der Programmablauf nicht beim
Delphi-Quellcode:
Result :=
vorbei kommt, da sich dann niemand dafür verantwortlich hält, dieses Objekt wieder freizugeben.

shmia 23. Aug 2012 16:41

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
So wäre es richtig:
Delphi-Quellcode:
class function TContainer.CreateContainer(...) : IContainer;
var BlaType : IBlaType;
begin
  Result := TContainer.Create(...);
  if FindBlaType(...) then
  begin
    Example.SetIrgendwas(BlaType);
  end
  else
    Result := nil;
end;

TiGü 23. Aug 2012 16:53

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
Also so sieht es eigentlich aus, musste aber Firmenspezifische Abkürzungen von ein paar Bezeichnern entfernen.
Die Kommentare sind von mir für euch zur Erklärung!

Delphi-Quellcode:
class function TNotifyEventContainer.CreateContainer(ANotification : HNotification; AExtMedium : IAExtMedium) : IEventContainer;
var
  EventType : IEventType;
  Event    : TNotifyEventContainer; //IEventContainer;
begin
  Result := nil;
  if Assigned(AExtMedium) then
  begin
    //in diesem Create steckt ein "leeres" Try-Except mit nur OutputDebugString um den Konstruktur-Code
    Event := TNotifyEventContainer.Create(ANotification, AExtMedium.GetAliasName, AExtMedium.GetGuid);    
   
   //FindEventType ist eine flache Funktion in der Unit.
   //GetEventTypes liefert eine IInterfaceList.
   //EventType ist ein out-Parameter, die Funktion sucht in der Liste anhand
   //von GetEventTypeID das betreffende IEventType in der IInterfaceList und gibt es zurück
    if Assigned(Event) and FindEventType(AExtMedium.GetEventTypes, Event.GetEventTypeID, EventType) then
    begin
      //setzt nur EventType als Field-Variable von TNotifyEventContainer
     Event.SetEventType(EventType);     
    end;
 
    Result := Event;
  end;
end;
Zur meiner Verteidigung muss ich sagen, dass das nicht von mir, sondern von meinen Vorgänger stammt.
Ich wäre hierrüber auch nie gestoßen, hätte ich nicht manchmal merkwürdige Effekte/Fehlermeldungen, seitdem ich FastMM4 installiert habe.
Der alte Speichermanager hatte wahrscheinlich beide Augen zugedrückt.

TiGü 23. Aug 2012 17:01

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1179546)
Wobei ich den Code eh nicht ganz verstehe, wenn ich ehrlich bin... Wieso sollte Assigned nach dem Create jemals False liefern?

Siehe neues Beispiel!

Zitat:

Create ist schon ein Konstruktor?
In so fern Create eine Exception wirft, wird diese ja nicht abgefangen -- außer du hast natürlich diesen Code entfernt ;)
Ja, ein normaler constructor Create;

Es kann schon passieren, dass beim Erstellen der Klasse irgendwas schiefgeht.
Darum hat mein Vorgänger um den Code im Create ein leeres try-except gestrickt.
Wenn es nun dadrin zur einer Exception kommt, dann existiert Example/Event nicht und jeder Zugriff drauf würde doch mit einer AV enden, oder?

TiGü 23. Aug 2012 17:10

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
Zitat:

Zitat von himitsu (Beitrag 1179548)
In dem gezeigten Code ist es so, daß hier nicht beide Referenzen "gleichzeitig" verwendet werden.
(oftmals macht man sowas, wenn auf eine Funktion des Objekts zugreifen will, welche man "vergessen" hat ins Interface aufzunehmen)

Dafür wird im betreffenden Projekt gerne das gemacht.
Wobei ich dabei auch immer irgendwie Bauchschmerzen habe.

Delphi-Quellcode:
TMyClass = class;

IMyInterface
  function GetObject : TMyClass;
end;

TMyClass = class(TInterfacedObject, IMyInterface)
  function GetObject : TMyClass;
end

...

function TMyClass.GetObject : TMyClass;
begin
 Result := self;
end;

himitsu 23. Aug 2012 17:19

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
:gelöscht:

himitsu 23. Aug 2012 17:39

AW: Verständnisfrage: Interface und dazugehöriges Objekt
 
Wenn etwas im Constructor schief geht, dann maximal eine Exception.

Also entweder wird der Constructor ausgeführt (keine Exception) und die Variable wird zugewiesen,
oder es knallt mit einer Exception raus, der variable wird nix zugewiesen, aber auch der nachfolgende Code wird nicht mehr ausgeführt.

Wenn das Programm zum IF kommt, sollte es immer etwas drinsteht, also niemals nil.
(es sei denn jemand überschreibt TObject.NewInstance und gibt dort bösartiger Weise NIL zurück, aber das ist soein Fall, den braucht man nicht beachten, denn wer sowas tut, der soll sich nicht wundern, wenn es dann wo anders knallt)

Delphi-Quellcode:
type
  TMyObject = class
    constructor Create;
  end;

constructor TMyObject.Create;
begin
  Abort; // raise Exception.Create('peng');
end;

var
  O: TObject;
begin
  O := TMyObject.Create;
  if Assigned(O) then
    ShowMessage('OK')
  else
    ShowMessage('nil'); // das sieht man niemals
Zitat:

Dafür wird im betreffenden Projekt gerne das gemacht.
Wobei ich dabei auch immer irgendwie Bauchschmerzen habe.
Das mit dem GetObject ist in aktuelleren Delphis schon enthalten.

Nur sieht man dort dieses "Interface" nicht direkt, da es kein echtes Interface ist, sondern eine statische GUID, welche im AS-Operator wie ein virtuelles Interface behandelt wird.
Delphi-Quellcode:
var
  i: IMyInterface;
  o: TMyObject;

o := i as TMyObject;
Delphi-Quellcode:
type
  IMyInterface = interface
    ['{C6936E1B-F0FE-42A0-A00E-39E90A66A9F0}']
    procedure Test;
  end;

  TMyObject = class(TInterfacedObject, IMyInterface)
    FText: string;
    constructor Create;
    procedure Test;
  end;

procedure TMyObject.Test;
begin
  ShowMessage(FText);
end;

constructor TMyObject.Create;
begin
  inherited;
  FText := 'Hallo Welt.';
end;

procedure TForm10.Button3Click(Sender: TObject);
var
  I: IMyInterface;
  O: TMyObject;
begin
  I := TMyObject.Create;
  O := I as TMyObject;
  O.Test;
  I := nil; // letzte und einzige Interfacereferenz vernichten
  O.Test;
end;
Aber wie gesagt, man muß wärend man auf dieses Objekt zugreift unbedingt verhindern, daß der Referenzzähler auf 0 geht, jedenfalls bei Interfaces welche sich über die Referenzzählung verwaltet werden.
Bei Onterfaces wo die Freigabe anders gesteuert wird, muß mn eben aufpassen, daß dieses ebenfalls nicht freigegeben wird.

PS: TComponent enthalten Interfaces, welche sich nicht selber freigeben.
Die Freigabe geschieht dort ausschließtlich über das Objekt (Free) und dem Free ist es egal, ob es irgendwo noch Referenzen auf dieses Interfaces/Objekt gibt.


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