Delphi-Version: 5
Mischen von Interface- und Klassenreferenzen
Hallo,
im neuen Blogartikel "Don't mix objects and interfaces" erläutert Dalija Prasnikar die Probleme die entstehen wenn Objekte und Interface vermischt werden. Ihre Codebeispiel sind überraschend, denn wie hier in der DP gelegentlich geschrieben wurde kann in Factorymethoden eine interne Instanz der Klasse erzeugt werden und am Ende eine Interfacereferenz zurückgegeben:
Delphi-Quellcode:
Laut Blogartikel ist allerdings das
function CreateFoo: IFoo;
var TmpFoo: TFoo; begin // Instanz der Klasse TmpFoo erzeugen, die von TInterfacedObject abgeleitet ist und IFoo implementiert TmpFoo := TmpFoo.Create(); // diverse Properties setzen die nicht Teil des IFoo Interface sind TempFoo.X := 1; // ... // Interface zurückgeben Result := TmpFoo; end; TmpFoo := TmpFoo.Create(); ein "huge no-no". Ist das Codebeispiel dennoch eine saubere Verwendung von Interfaces? |
AW: Mischen von Interface- und Klassenreferenzen
In diesem speziellen Fall sollte es keine Probleme verursachen.
Gefährlich ist aber z.B. sowas:
Delphi-Quellcode:
In dem Fall würde es crashen, weil intern grob folgendes passiert:
procedure DoSomethingWithFoo(Foo: IFoo);
begin end; function CreateFoo: IFoo; var TmpFoo: TFoo; begin TmpFoo := TmpFoo.Create(); TempFoo.X := 1; DoSomethingWithFoo(TmpFoo); Result := TmpFoo; end;
Delphi-Quellcode:
Es muss natürlich nicht genau an der Stelle crashen, sondern kann auch irgendwann später crashen.
procedure DoSomethingWithFoo(Foo: IFoo);
begin end; function CreateFoo: IFoo; var TmpFoo: TFoo; TmpInterface: IFoo; begin TmpFoo := TmpFoo.Create(); // RefCount = 0 TempFoo.X := 1; TmpInterface := TmpFoo; // inc(RefCount) (= 1) DoSomethingWithFoo(TmpInterface); TmpInterface := nil; // dec(RefCount) (= 0) -> TmpFoo.Free; Result := TmpFoo; // inc(RefCount)... crash, weil Objekt schon freigegeben ist end; |
AW: Mischen von Interface- und Klassenreferenzen
Die Äußerungen beziehen sich auch nur auf den Fall, wo auch ein Reference-Counting stattfindet - genauer dieses zum Destroy der Instanz führt. Dies gilt z.B. bei allen Abkömmlingen von
Delphi-Quellcode:
. Damit im Create einer solchen Klasse das RefCounting keinen Ärger macht (dort gibt es nämlich noch keine Interface-Referenz, die das Objekt am Leben halten könnte), wird im
TInterfacedObject
Delphi-Quellcode:
der RefCount künstlich auf 1 gesetzt:
TInterfacedObject.NewInstance
Delphi-Quellcode:
Dies wird dann im AfterConstruction wieder zurückgenommen:
// 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;
Delphi-Quellcode:
Die darauf folgende Zuweisung auf eine Interface-Referenz sorgt dann wieder für das korrekte RefCounting.
procedure TInterfacedObject.AfterConstruction;
begin // Release the constructor's implicit refcount AtomicDecrement(FRefCount); end; Beim Destroy gibt es aber eine sehr ähnliche Problematik, wenn z.B. im Destroy irgendwelche RefCounting relevante Befehle ausgeführt werden. Dort wählt man aber mittlerweile (seit XE7 - in älteren Versionen crasht das noch) einen anderen Weg über ein Flag namens objDestroyingFlag. Wie immer muss man halt genau wissen was man tut. |
AW: Mischen von Interface- und Klassenreferenzen
Namenloser hat das richtig dargestellt.
Die Factorymethode hat nur eine Aufgabe, das Interface-Objekt zu erstellen. Das fertige Objekt als Interface zu verwenden, gehört nicht zu dieser Aufgabe. Wenn das erfoderlich ist, sollte man in zwei Methoden trennen.
Delphi-Quellcode:
function CreateFoo: IFoo;
var TmpFoo: TFoo; begin // Instanz der Klasse TmpFoo erzeugen, die von TInterfacedObject abgeleitet ist und IFoo implementiert TmpFoo := TmpFoo.Create(); // diverse Properties setzen die nicht Teil des IFoo Interface sind TempFoo.X := 1; // ... // Interface zurückgeben Result := TmpFoo; end; function CreateAndInitFoo: IFoo; begin Result := CreateFoo; DoSomethingWithFoo(Result); end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:43 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