Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Instanz Klasse einfach weg (https://www.delphipraxis.net/199604-instanz-klasse-einfach-weg.html)

freimatz 5. Feb 2019 12:08

Delphi-Version: 5

Instanz Klasse einfach weg
 
Hallo,
im Beitrag Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen? hatte ich etwas eingeworfen was dann etliche lange Antworten gegeben haben, die allerdings nicht zu dem Thema gehörten. Daher nochmals ein neuer Beitrag.
An das Problem mit Mischung von Objekt- und Interface-Referenz hatte ich zuerst gedacht. Das war es aber nciht.

Die Ursache für mein Problem ist mit inzwischen bekannt und hat mehrere Ursachen. Hier ein Beispiel - extremst vereinfacht:
Delphi-Quellcode:
TBlub = class (TInterfacedObject, IBlub1, IBlub2);

//Irgend wo steht dann
var blub : IBlub1;
begin
blub := TBlub.Create() as IBlub1;
...
end;

//weiter:
procedure TBlub.Initialize(); //override
var blbl: IBlub2;
begin
  blb := Self as IBlub2; //Hier erhöht sich der Referenzzähler
  Self.Egal := irgeeinservice.CreateEtwas(blbl);
  blbl := Nil; //Hier erniedrigt sich der Referenzzähler
end;

procedure TBlub.AfterConstruction();//override
begin
  // Call AfterConstruction of base class
  inherited; //Da drin geht der Referenzzähler auf 0

  // Initialize member of descendant class
  self.Initialize();

  //...
end;
Soweit der hoffentlich sinnvolle Auszug.
Ich finde es an sich ja schon logisch, dass man in AfterConstruction zuerst inherited aufruft und dann erst das eigene macht.

1. Problem (meinte der Kollege): Warum macht man so viel Dinge im Konstruktor. Der sollte schlank bleiben.
2. Ein Hack in der RTL den ich für nicht so dolle finde:
Delphi-Quellcode:
procedure TInterfacedObject.AfterConstruction;
begin
// Release the constructor's implicit refcount
  AtomicDecrement(FRefCount);
end;

// Set an implicit refcount so that refcounting during construction won't destroy the object.
class function TInterfacedObject.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  TInterfacedObject(Result).FRefCount := 1;
end;
Mein Problem habe ich nun gelöst, indem ich das mein Ding nicht mehr im Konstruktor mache sondern später erst.
Aber: Wie hat sich der Erfinder das gedacht?

Schokohase 5. Feb 2019 12:25

AW: Instanz Klasse einfach weg
 
Ja, das ist so.

Du kannst das wie folgt umgehen:
Delphi-Quellcode:
//weiter:
procedure TBlub.Initialize(); //override
begin
  DoInitialize(Self);
end;

procedure TBlub.DoInitialize(const blb: IBlub2);
begin
  Self.Egal := irgeeinservice.CreateEtwas(blbl);
end;
oder alternativ
Delphi-Quellcode:
//weiter:
procedure TBlub.Initialize(); //override
var blbl: IBlub2;
begin
  blb := Self as IBlub2; //Hier erhöht sich der Referenzzähler
  Self.Egal := irgeeinservice.CreateEtwas(blbl);
  blbl := Nil; //Hier erniedrigt sich der Referenzzähler
end;

procedure TBlub.AfterConstruction();//override
begin
  // Call AfterConstruction of base class
  inherited; //Da drin geht der Referenzzähler auf 0

  // Initialize member of descendant class
  //self.Initialize();

  //...
end;

constructor TBlub.Create;
begin
  inherited;
  Self.Initialize();
end;
Was dein Kollege meint ist schon richtig, du machst es aber nicht richtiger, wenn du den Code nur in anderen Methoden verlagerst und diese trotzdem im Rahmen des Konstruktors ausführst. Dadurch wird der Konstruktor der Klasse selber nicht schlanker, denn der Code wird ja trotzdem ausgeführt. Es ist einfach nur Augenwischerei.

Uwe Raabe 5. Feb 2019 12:38

AW: Instanz Klasse einfach weg
 
Zitat:

Zitat von freimatz (Beitrag 1424741)
Aber: Wie hat sich der Erfinder das gedacht?

Na ja, er hat sich gedacht, daß vielleicht jemand im Create irgendwas mit der Interface-Referenz macht, das den Referenzzähler hoch und wieder runter zählt. Wäre der Referenzzähler bei Beginn des Create = 0 würde das beim Runterzählen ein unerwünschtes Destroy auslösen. Da das NewInstance direkt vor und das AfterContstruction direkt nach der Create-Kette ausgeführt wird, erhöht er den RefCount vorher künstlich und erniedrigt ihn hinterher ohne das Destroy auszulösen (also eben nicht mit _AddRef bzw. _Release).

Er hat vermutlich nicht damit gerechnet, daß jemand das AfterConstruction überschreibt und darin außerhalb dieser Klammer (also nach dem inherited) mit dem Interface rumhantiert.

Übrigens: Für das Destroy gibt es so eine Sicherung interessanterweise nicht. Hantieren mit Interfaces innerhalb des Destroy eines TInterfacedObject ist also ebenfalls zum Scheitern verurteilt. Deswegen arbeite ich in der Regel mit einer eigenen Ableitung, die lediglich das BeforeDestruction ergänzt und den RefCount auf 1 setzt:
Delphi-Quellcode:
begin
  inherited;
  FRefCount := 1;
end;

Zitat:

Zitat von freimatz (Beitrag 1424741)
Ich finde es an sich ja schon logisch, dass man in AfterConstruction zuerst inherited aufruft und dann erst das eigene macht.

Das kann man so pauschal nicht sagen. Je nach Situation kann/soll/muss das inherited vorher, hinterher oder zwischendrin aufgerufen werden - manchmal sogar gar nicht.


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