![]() |
Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Hallo,
ich bin mir aktuell etwas unsicher, was die Speicherung und Weitergabe von Interface-Verweisen anbelangt. Ist das erlaubt oder wird mir hier bei der Weiterreichung der "Zeiger" kaputt gemacht wg. Referenzzählung? Ich versuche mal, das ganze hier (stark vereinfacht) darzustellen... Ich habe Schnittstellendefinitionen, z.B.:
Delphi-Quellcode:
ITestGet = interface[ '{725D0235-8604-4E26-88E4-29223A3E6EB1}' ]
function GetTestName() : String; property TestName : String read GetTestName; end; ITestSet = interface( ITestGet )[ '{2F54CC2F-70A8-4621-99C6-2B1CD5AE079F}' ] procedure SetTestName( const sName_ : String ); property TestName : String write SetTestName; end; ITestImpl = interface( ITestGet )[ '{1C48CB92-1DA9-452E-9921-9B43A598715A}' ] procedure SetIfTestImpl( ifTestImpl_ : ITestImpl ); function GetIfTestImpl() : ITestImpl; property IfTestImpl : ITestImpl read GetIfTestImpl write SetIfTestImpl; end; Dann habe ich folgende Klassen:
Delphi-Quellcode:
TMyBaseForm = class( TForm )
public constructor Create( owner_ : TComponent ); override; constructor CreateMe( owner_ : TComponent ); virtual; procedure SetIfTestImpl( ifTestImpl_ : ITestImpl ); virtual; function GetIfTestImpl() : ITestImpl; virtual; function GetIsIfTestImplSelf() : Boolean; virtual; property IfTestImpl : ITestImpl read GetIfTestImpl write SetIfTestImpl; property IsIfTestImplSelf : Boolean read GetIsIfTestImplSelf; private _ifTestImpl : ITestImpl; end; //*************************************************************************** TMyOtherForm = class( TMyBaseForm ) published pbTest : TButton; public constructor CreateMe( owner_ : TComponent ); override; end; //*************************************************************************** TMyMainBaseForm = class( TMyBaseForm, ITestImpl ) public constructor CreateMe( owner_ : TComponent ); override; procedure SetIfTestImpl( ifTestImpl_ : ITestImpl ); override; function GetTestName() : String; virtual; property TestName : String read GetTestName; end; //*************************************************************************** TMyMainForm = class( TMyMainBaseForm, ITestSet ) public constructor CreateMe( owner_ : TComponent ); override; function GetTestName() : String; override; procedure SetTestName( const sName_ : String ); virtual; property TestName : String read GetTestName write SetTestName; private _sTestName : String; end; Hier die Implementierungen:
Delphi-Quellcode:
In der Art wie TMyOtherForm gibt es ganz viele Klassen, so wie TMyMainForm gibt es normalerweise nur eine; wenn die MyOtherForm-Klassen instanziert werden, existiert die Instanz von TMyMainForm bereits und sie erhalten als Owner diese Instanz.
constructor TMyBaseForm.Create(
owner_ : TComponent ); begin CreateMe( owner_ ); end; constructor TMyBaseForm.CreateMe( owner_ : TComponent ); var ifTestImplMy : ITestImpl; begin _ifTestImpl := nil; if ( Assigned(owner_) and Supports(owner_, ITestImpl, ifTestImplMy) ) then begin SetIfTestImpl( ifTestImplMy ); end; end; procedure TMyBaseForm.SetIfTestImpl( ifTestImpl_ : ITestImpl ); begin _ifTestImpl := ifTestImpl_; end; function TMyBaseForm.GetIfTestImpl() : ITestImpl; begin Result := _ifTestImpl; end; function TMyBaseForm.GetIsIfTestImplSelf() : Boolean; var ifTestImplMy : ITestImpl; begin Result := false; if ( Assigned(_ifTestImpl) ) then begin if ( Supports(Self, ITestImpl, ifTestImplMy) ) then begin if ( _ifTestImpl = ifTestImplMy ) then begin Result := true; end; end; end; end; //*************************************************************************** constructor TMyOtherForm.CreateMe( owner_ : TComponent ); var ifTestImplMy : ITestImpl; begin inherited CreateMe( owner_ ); pbTest := TButton.Create( Self ); pbTest.Caption := 'Unknown'; ifTestImplMy := IfTestImpl; if ( Assigned(ifTestImplMy) ) then begin pbTest.Caption := ifTestImplMy.TestName; end; end; //*************************************************************************** constructor TMyMainBaseForm.CreateMe( owner_ : TComponent ); var ifTestImplMy : ITestImpl; begin inherited CreateMe( owner_ ); ifTestImplMy := GetIfTestImpl(); if ( NOT Assigned(ifTestImplMy) ) then begin SetIfTestImpl( nil ); end; end; procedure TMyMainBaseForm.SetIfTestImpl( ifTestImpl_ : ITestImpl ); var ifTestImplMy : ITestImpl; begin ifTestImplMy := ifTestImpl_; if ( NOT Assigned(ifTestImplMy) ) then begin Supports( Self, ITestImpl, ifTestImplMy ); end; inherited SetIfTestImpl( ifTestImplMy ); end; function TMyMainBaseForm.GetTestName() : String; var ifTestImplMy : ITestImpl; begin Result := EmptyStr; ifTestImplMy := GetIfTestImpl(); if ( Assigned(ifTestImplMy) then begin if ( IsIfTestImplSelf ) then begin Result := Format( '%s.%s', [Classname, Name] ); end else begin Result := ifTestImplMy.GetTestName(); end; end; end; //*************************************************************************** constructor TMyMainForm.CreateMe( owner_ : TComponent ); begin inherited CreateMe( owner_ ); _sTestName := EmptyStr; end; function TMyMainForm.GetTestName() : String; begin if ( Length(_sTestName) > 0 ) then begin Result := _sTestName; end else begin Result := inherited GetTestName(); end; end; procedure TMyMainForm.SetTestName( const sName_ : String ); begin _sTestName := sName_; end; Ich habe jetzt das Problem, dass manchmal bei den Aufrufen von GetIfTestImpl bzw. dem Property IfTestImpl als Ergebnis nil zurück kommt, obwohl die interne Klassenvariable _ifTestImpl gesetzt ist. Ich habe da bereits in der Methode eine Log-Ausgabe der Art Format( '%8.8X', [Integer(_ifTestImpl)] ) die zeigt eine Adresse an - wenn ich das ganze dann nach zurückkehren der Methode im Aufrufer mit dem zugewiesenen Ergebnis mache, kommt 0 als Ergebnis --> deswegen meine Frage: ist es erlaubt, Interface-Zeiger in einer Klassenvariablen zu speichern und "herumzureichen"? (das ganze wie immer bei mir für Delphi7 :wink:) |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Du hast dort gar keine Referenzzählung, denn du leitest von TForm ab. In den visuellen Komponenten sind zwar die Interface-Routinen vorhanden, so dass du direkt Interfaces implementieren kannst, aber eben ohne Referenzzählung.
Wenn du also ein solches Objekt in einer Interfacereferenz speicherst und das Objekt freigegeben wird, zeigt die Interfacereferenz auf ein ungültiges Objekt. Allerdings ist sie deshalb nicht 0, was genau das Problem an der Stelle ist. Deshalb muss man dort aufpassen, was man tut. Zitat:
|
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Eigentlich ja, aber nein.
TComponent besitzt eine Standardimplementation für Interfaces und in Dieser ist die Referenzzählung des Interfaces inktiv. Interfaces auf TComponent sollten daher immer nur kurzfristig verwendet und dann freigegeben (NIL gesetzt, bzw. Variable läuft aus dem Scope). |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Zitat:
Mir kommt es so vor, als ob durch das Verlassen der Get-Methode der Interface-Zeiger abgeräumt und auf nil gesetzt wird... Zitat:
Da muss noch ein anderes (für mich gerade nicht sichtbares) Problem bestehen - da kommt jetzt bei mir beim Start im Debugger eine Access-Violation; allerdings ohne, dass ich den Call-Stack sehen kann :-( Ich habe das Projekt mal als .zip angehängt... |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Zitat:
Wenn ich das in einer Klassenvariablen speichere, verliert es doch seinen Scope erst dann, wenn die Klasse abgeräumt wird, oder? Gibt es eigentlich einen Unterschied (im Ergebnis-Zeiger) bei den folgenden Methoden, die Variable myIf zu setzen (Voraussetzung ist natürlich, dass MyClassInst das Interface implmentiert)?
Delphi-Quellcode:
var myIf : IMyInterface;
myIf := MyClassInst as IMyInterface; myIf := IMyInterface( MyClassInst ); Supports( MyClassInst, IMyInterface, myIf ); |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Das macht Delphi der immer, Managed Types automatisch aufräumen.
Interface, Variant, LongStrings (WideString und alle Delphi-Strings, außer ShortString-Typen) und dynamische Arrays. Im Falle von Interface-Variablen wird bei <>nil ein intf._Release ausgeführt und die Variable quasi auf nil gesetzt. Wurde also das TObjekt bereits freigegeben (Destroy/Free aufgerufen, oder dessen Owner oder Parent wurde freigegeben) und nachfolgend wird der Variable was neues zugewiesen oder die Variable wird Freigegeben (z.B. läuft aus dem Scope), dann täte es im _Release knallen. Alternativ kein Interface auf das TComponente selbst, sondern IM Component ein Interface, also z.B. TInterfacedPersistent als Vorfahre. (ich glaub es gab auch einen TComponentNachfahre, der sowas hat) |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
In einer aktuelleren Version (hier die Community Edition) siehst du die Ursache: Anhang 56673 Ursache:
Delphi-Quellcode:
Besser:
constructor TMyMainForm.CreateMe( owner_ : TComponent );
begin inherited Create( owner_ );
Delphi-Quellcode:
Wenn sich die Methodensignatur des geerbten Aufrufs nicht ändert, sollte man nur inherited schreiben. An anderer Stelle fehlte das inherited, so dass die Initialisierung des Formulars fehlte.
constructor TMyMainForm.CreateMe( owner_ : TComponent );
begin inherited; Grundregel: Wenn es irgendwie anders geht, sollte man Konstruktoren NIE NIE NIE anders als Create nennen. Das ist wirklich nur eine Notlösung (und den Sinn verstehe ich hier nicht, da es zumindest in dem gekürzten Beispiel ohne viel einfacher geht). Ein weiterer Fehler: FormCreate und btOKClick waren unter public statt unter published (weshalb diese Eventhandler automatisch direkt nach der Klassendeklaration erzeugt werden, wo sie published sind, auch ohne das Wort). Unter public können sie aber nicht gefunden werden. Ansonsten kann ich keine Probleme feststellen, auch nicht mit Delphi 7. Was muss ich tun? Zitat:
|
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Ja, seit einer ganzen Weile existiert ein virtuelles Interface, welches TInterface nach TObject casten kann, z.B. einfach mit AS. (seit mindestens D2009/XE)
Ansonsten implementiert TComponent schon seit vielen Jahren auch noch ein IInterfaceComponentReference, welches eine Methode GetComponent bietet. ![]() |
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Zitat:
Zitat:
...und ich kann doch den Standard "Create"-Konstruktor nicht als virtuell überschreiben / neu schreiben, da dieser nicht virtuell ist - oder geht das irgendwie? Deswegen der neue Konstruktor "CreateMe" in TMyBaseForm und eine "Umlenkumg" auf diesen im Standard-Konstruktor "Create"... Oder verstehe ich da was falsch? Was aber tatsächlich gefehlt hatte, war das inherited in TMyBaseForm auf den Basis-Konstruktor von TForm:
Delphi-Quellcode:
constructor TMyBaseForm.CreateMe( owner_ : TComponent );
var ifTestImplMy : ITestImpl; begin inherited Create( owner_ ); _ifTestImpl := nil; if ( Assigned(owner_) and Supports(owner_, ITestImpl, ifTestImplMy) ) then begin SetIfTestImpl( ifTestImplMy ); end; end; Zitat:
Ich habe den Sinn vom "published" sowieso noch nie so richtig verstanden... :oops: Zitat:
Also muss im realen Programm noch irgendwas anders / komplizierter sein - ich muss da nochmal genauer schauen... Zitat:
|
AW: Verweis auf Interface-Instanz weitergeben - ist das erlaubt?
Zitat:
siehe TObject.Create und TComponent.Create :angle: Ob man beim "Verdecken" micht einem neuen Contructor noch ein reintroduce benötigt, das mehrkt man dann schon. Bei gleicher Signatur (Parameter) geht nur verdecken. Und ansonsten muß man noch überlegen, ob ein overload benötigt wird, um alternativ auch die Constructoren der Vorfahren aufrufen zu können. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:26 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz