![]() |
Interface referenzen auf gleichheit prüfen?
Moin,
ich bin gezwungen (von mir selbst) diverse interface-referenzen (COM) zu vergleichen, was sich als schwer heraustellt, da verschiednene ITypen verschiedene binäre-adressen auf ein und das selbe objekt haben. Stark vereinfachter versuchsaufbau:
Delphi-Quellcode:
das ergebnis wäre zB:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type // test typen iA = interface procedure A; end; TA= class(TInterfacedObject,iA) public procedure A; end; iB = interface(iA) procedure B; end; TAB = class(TA,iB) public procedure B; end; {*********************************************} TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; Button3: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } fA:iA; fB:iB; fRef:TAB; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin FRef := TAB.Create; // object erzeugen, das einzige fB := fRef; // ref 1 fA := fRef; // ref 2 end; procedure TForm1.Button1Click(Sender: TObject); begin // hier sieht man, dass alle drei referenzen verschiedene addressen haben... memo1.Lines.Add(format('ref = %p | a = %p | b = %p',[pointer(fRef),pointer(fA),pointer(fB)])); // zB so: ref = 008D24E4 | a = 008D24F0 | b = 008D24F4 end; { TA } procedure TA.A; begin ShowMessage('A'); end; { TAB } procedure TAB.B; begin ShowMessage('B'); end; procedure TForm1.Button2Click(Sender: TObject); begin fA.A; end; procedure TForm1.Button3Click(Sender: TObject); begin FB.B; FB.A; end; end.
Code:
und ich sehe auch ein, dass das intern für die COM implementierung irgendwie so sein muss...
ref = 008D24E4 | a = 008D24F0 | b = 008D24F4
...ABER wie vergleicht man diese referenzen so, dass TRUE bei rumm kommt? Danke, in erwartung :-D |
Re: Interface referenzen auf gleichheit prüfen?
Du könntest ein weiteres Interface einführen:
Delphi-Quellcode:
Alle Klassen sollten dann dieses Interface implementieren:
ICompareRef = interface
function GetCookie:Integer; end;
Delphi-Quellcode:
Und so wird's benutzt
TA= class(TInterfacedObject,iA, ICompareRef)
private FCookie : Integer; protected function GetCookie:Integer; public constructor Create; procedure A; end; function TA.GetCookie:Integer; begin Result := FCookie; end; constructor TA.Create; begin // GlobalCookieCounter ist eine globale Variable // sie beim Erzeugen eines Objekte um 1 hochgezählt FCookie := InterlockedIncrement(GlobalCookieCounter); end;
Delphi-Quellcode:
if (fA as ICompareRef).GetCookie=(fB as ICompareRef).GetCookie then
ShowMessage('die Dinger sind gleich'); |
Re: Interface referenzen auf gleichheit prüfen?
Ok, du meinst ich soll allen objekten eine eindeutoge ID geben. Das wäre IMO schon fast ein overkill, da ein objekt ja eigentlich eindeutig ist... Dann könnte ich eigentlich an stelle der ID auch die Object-referenz zurück liefern, denn wenn ich erstmal in dem TObject bin, dann ist self natürlich immer gleich.
Ich dachte eigentlich eher an eine native delphi funktionen oder so? Sowas wie
Delphi-Quellcode:
Das ist bestimmt möglich!?
function isSameIntfObject(const i1,i2:IInterface):boolean;
//edit: equal <> same |
Re: Interface referenzen auf gleichheit prüfen?
Ich vermute mal das du in deinem Konzept einen Denkfehler hast.
Verschiedene Objecte können mehrere verschiedene Interfaces implementieren. Zb.
Delphi-Quellcode:
Nun hast du mehrere allozierte Interfaces obiger Typen und möchtest wissen ob dieses das Interface A verwenden. Das geht indem man GUIDs benutzt, denoch könnten dann "gleiche" Objecte die das IInterface_A implementieren der Klassen TClass_AB und TClass_AC angehören, d.h. der Implementor des Interfaces ist denoch nicht gleich.
TClass_AB = class(TInterfacedObject, IInterface_A, IInterface_B)
end; TClass_AC = class(TInterfacedObject, IInterface_A, IInterface_C) end; TClass_BC = class(TInterfacedObject, IInterface_B, IInterface_C) end; Willst du aber nun überprüfen ob die Implementierenden Klassen identisch sind dann musst du selber eine Schnittstelle dafür programmieren. Dazu könntest du das Delphi Klassen Konzept benutzen, etwa so:
Delphi-Quellcode:
Gruß Hagen
type
IImplementor = interface ['0896943-093893-498574'] // <- deine GUID hier eintragen !! in IDE [STRG+ALT+G] function Object: TObject; end; TClass_AB = class(TInterfacedObject, IImplementor, IInterface_A, IInterface_B) end; TClass_AC = class(TInteefacedObject, IImplementor, IInterfase_A, IInterface_C) end; function TClass_AB.Object: TObject; begin // Result := Self; // <- da EAX schon Self enthält, und EAX das Result ist reicht eine leere Funktion hier aus !! end; function Compare(const A,B: IInterface): Boolean; var AI,BI: IImpelementor; begin Result := (A.QueryInterface(AI, IImplementor) = H_OK) and (B.QueryInterface(BI, IImplementor) = H_OK) and (AI.Object.ClassType = BI.Object.ClassType); end; |
Re: Interface referenzen auf gleichheit prüfen?
Hallo Maximov,
wie Hagen bereits dargestellt hat, handelt es sich bei Schnittstellen (Interfaces) um unterschiedliche "Sichten" auf ein und dasselbe Objekt. Weil die Identität eines Objekts jedoch ein Wesensmerkmal darstellt, scheint es tatsächlich überaschend, dass der von Dir diskutierte Code bei einen Vergleich der Art
Delphi-Quellcode:
den Wert False zurückgibt.
myObj := TMyClass.Create;
myRefToInterfaceA := myObj; myRefToInterfaceBA := myObj; Result := Pointer(myRefToInterfaceA) = Pointer(myRefToInterfaceB); Tatsächlich ist dies lediglich spezifisch für Wahl der Implementierung von Interfaces im COM-Umfeld, bei der jede Klasse für jedes implementierte Interface seine eigene "Methodentabelle" bereitstellt, auf die (indirekt) durch die Referenz auf ein Interface verwiesen wird. Obgleich es möglich ist, Schnittstellen voneinander erben zu lassen und Zuweisungen der Art
Delphi-Quellcode:
durchzuführen, obwohl die Klasse TMyClass aus dem Beispiel das Interface IMyInterface selbst nicht implementiert, ist eine Zuweisung der Art
type
IMyInterface = interface ['{A GUID}'] end; IMyInterfaceDescendant = interface(IMyInterface) ['{Another GUID}'] end; TMyClass = class(TInterfacedObject, IMyInterfaceDescendant) end; var myObject: TMyClass; myRefToInterface: IMyInterface; myRefToInterfaceDescendant: IMyInterfaceDescendant; begin myObject := TMyClass.Create; myRefToInterfaceDescendant := myObject; myRefToInterface := myRefToInterfaceDescendant;
Delphi-Quellcode:
nicht zulässig wie auch der Cast
myRefToInterface := myObject
Delphi-Quellcode:
zu einer Exception führt: Das Interface IMyInterface wird von TMyClass nicht implementiert, es kann keine Referenz auf die "Methodentabelle" zurückgegeben werden.
myObject as IMyInterface
Was Du also machen kannst, wenn Du ohne "Identitätsinterface", wie Hagen es vorschlägt, an die Identität der Objekte zweier unterschiedlicher Interfaces herankommen möchtest, ist der Cast auf auf ein gemeinsames Interface, z.B. IInterface, und der anschließende Vergleich der Art
Delphi-Quellcode:
Achtung: Wie oben beschrieben sind Zuweisungen zu Vorfahren-Interfaces gültig, also im Speziellen Zuweisungen zum Wurzelinterface IInterface. Ein Vergleich nach
Result := (myRefToAnInterface as IInterface) = (myRefToAnotherInterface as IInterface)
Delphi-Quellcode:
kann demnach zu fehlern führen!
var
myHelper1: IInterface; myHelper2: IInterface; begin myHelper1 := ARefToAnInterface; myHelper2 := ARefToAnnotherInterface; Result := myHelper1 = myHelper2; |
Re: Interface referenzen auf gleichheit prüfen?
@Choose:
der Vergleich
Delphi-Quellcode:
ist nicht zwangsläufig durchführbar.
Result := (myRefToAnInterface as IInterface) = (myRefToAnotherInterface as IInterface)
Angenommen:
Delphi-Quellcode:
dann würde obiger Vergleich TRUE liefern auch wenn man ein Object vom Typ TClassA mit TClassB vergleicht, denn beide basieren in ihrer Impelementierung von IInterface_A auf TClassBase. Man hätte also nur die Information das beide Objecte die IInterface_A Schnittstelle auf gleicher Basis impelemntiert haben. Da aber TClass_B zB. die Methoden von IInterface_A intern überschrieben haben können, wären auch untrschiedliche Implementierungen vom IINterface_A möglich. Der Vergleich würde als TRUE zurückliefern obwohl es sich a.) um unterschiedliche Klassen handelt und b.) deren Implementierungen unterschiedlich wären.Result := (myRefToAnInterface as IInterface_A) = (myRefToAnotherInterface as IInterface_A) und type TClassBase = class(TInterfacedObject, IInterface_A) end; TClassA = class(TClassBase, IInterface_B) end; TClassB = class(TClassBase, IInterface_C) end; Somit wäre das Result TRUE logisch gesehen eine Falschaussage. Gruß Hagen |
Re: Interface referenzen auf gleichheit prüfen?
Hallo Hagen,
nach Deinem Ansatz von oben und der daraus abgeleiteten Implementierung:
Delphi-Quellcode:
ergibt der Vergleich
type
IMyBaseInterface = interface ['{3F3E45E4-3FD4-4326-A2FE-637339B4E8A9}'] end; IMyInterfaceA = interface end; IMyInterfaceB = interface end; TMyBaseClass = class(TInterfacedObject, IMyBaseInterface) end; TMyClassA = class(TMyBaseClass, IMyInterfaceA) end; TMyClassB = class(TMyBaseClass, IMyInterfaceB) end; var myRefToInterfaceA : IMyInterfaceA; myRefToInterfaceB : IMyInterfaceB; myCompareRefA : IMyBaseInterface; myCompareRefB : IMyBaseInterface; begin myRefToInterfaceA := TMyClassA.Create; myRefToInterfaceB := TMyClassB.Create; myCompareRefA := myRefToInterfaceA as IMyBaseInterface; myCompareRefB := myRefToInterfaceB as IMyBaseInterface;
Delphi-Quellcode:
entgegen Deiner Aussage immer False, weil die beiden erzeugten Exemplare (unabhängig davon, ob sie Exemplare unterschiedlicher Klassen, einen gemeinsamen Vorfahren, der besagtes Interface IMyBaseInterface implementiert, besitzen oder derselben Klasse angehören), weil sie voneinandert verschiedene Identitäten haben.
Result := myCompareRefA = myCompareRefB
Innerhalb einer Schnittstellenreferenz ist (indirekt) die "Methodentabelle" der Klasse zur Realisierung des Interfaces und die Identität des Objekts codiert. Wäre dies nicht so und zeigte myCompareRefA für jedes Exemplar der Klasse TMyClassA auf dieselbe Adresse, wäre die Verwendung der Pseudovariablen Self innerhalb von so referenzierten Methoden nicht möglich... Ich hoffe, dass ich Dich richig verstanden und nicht am Thema vorbeigeschrieben habe :gruebel: |
Re: Interface referenzen auf gleichheit prüfen?
Nene, ich meinte das die gemeinsamme Vorfahrklasse das zu vergleichende Interface implementieren. Also:
Delphi-Quellcode:
Beide Klassen, B und C impelementieren auf Grund von Vererbung auch IInterface_A.
TClassBase = class(..., IInterface_A)
TClass_B = class(TClassBase, IInterface_B) TClass_C = class(TClassBase, IInterface_C) Ein Vergleich von Objecten der Klassen B und C auf das Interface IInterface_A würde demnach TRUE ergeben. Ich müsste das aber auch noch erst praktisch überprüfen. Mein Grundgedanke lief aber daraus hinaus das nur die Klasse TClassBase in ihrer RTTI die VMT für IInterface_A im Codesegment speichert. Die Klassen A und B wiederum erben diese VMT indirekt durch die Verebung von TClassBase. Aber egal, wenn man meinen obigen Vorschlag benutzt so ist man definitiv auf der sauberen Seite, denn das muss immer korrekt funktionieren da wir eben nicht auf interne und undokumentirte Implementierungsdetails des Delphi Compilers und dessen RTTI/VMTs aufsetzen. Generell muß man eben wissen das ein Interface nur eine reine Deklaration "wie was" sein sollte, aber eben nicht "wo was" tatsächlich ist, darstellt. Somit kann man bei einer Variable nur überprüfen ob sie ein Interface X unterstützt aber nicht wie, wo und wer sie tatsächlich implementiert. Somit kann man auf Grund des Interface Konzeptes nicht ermitteln ob die Implemntation == Delphi Klasse/Object identische Typen sind. Mit meinem Vorschlag wird aber genau das ermöglicht, ohne das man sich auf undokumentierte Funktinalitäten eines Compilers verlassen muß. Gruß Hagen |
Re: Interface referenzen auf gleichheit prüfen?
Hey Hagen,
ich glaube, dass wir aneinander vorbeischreiben, vielleicht könnte Maximov an dieser Stelle seine Anfrage richtigstellen. Wenn ich ihn richtig verstanden habe, möchte er feststellen, ob es sich bei zwei Objekten, die sich hinter Interfacereferenzen unterschiedlichen Typs verbergen, um ein und dasselbe Exemplar (und damit implizit derselben Klasse, weil Delphi keine Mehrfahvererbung unterstützt) handelt. Du, Hagen, scheinst jedoch zeigen zu wollen, ob die Klassen zweier Exemplare hinter zweier Interfacereferenzen und nur die Klassen identisch sind, ohne auf die Identität der Exemplare einzugehen. Ohne Interfaces also
Delphi-Quellcode:
bzw.
//prüfen, ob Objekte identisch
Result := AnObject = AnotherObject;
Delphi-Quellcode:
Bitte korrigiere micht jemand, wenn ich falsch liege.
//prüfen, ob Klassen identisch
Result := AnObject.ClassType = AnotherObject.ClassType; |
Re: Interface referenzen auf gleichheit prüfen?
Ja und, schau dir obigen Code mal genaur an ;)
Delphi-Quellcode:
vergleicht die Klassen, und daraus wird
function Compare(const A,B: IInterface): Boolean;
var AI,BI: IImpelementor; begin Result := (A.QueryInterface(AI, IImplementor) = H_OK) and (B.QueryInterface(BI, IImplementor) = H_OK) and (AI.Object.ClassType = BI.Object.ClassType); end;
Delphi-Quellcode:
um die zu implementierenden Objecte zu vergleichen. Bei TRUE wird das Interface in A und B durch das selbe Object implementiert, ergo A und B stellen das selbe Object dar, auch WENN eben Pointer(A) == Pointer(B) FALSE ist. Ich hätte gedacht das nach dem Studium meines obigen Postings das eigentlich trivial ist ;) und jeder das so ableiten kann.
function Compare(const A,B: IInterface): Boolean;
var AI,BI: IImpelementor; begin Result := (A.QueryInterface(AI, IImplementor) = H_OK) and (B.QueryInterface(BI, IImplementor) = H_OK) and (AI.Object = BI.Object); end; Da wir das absichtlich so konstruiert haben muß das funktionioren, es basiert also nicht auf undokumentierten Annahmen wie Delphis Compiler intern was macht. Gruß Hagen |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:33 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 by Thomas Breitkreuz