Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Gleiche Interface-Referenzen? (https://www.delphipraxis.net/184186-gleiche-interface-referenzen.html)

himitsu 5. Mär 2015 23:35

Gleiche Interface-Referenzen?
 
n'abend,

ist es nur Zufall, oder haben abgeleitete Interfaces in den Objekten wirklich "immer" die selben Referenzen?
Auch in anderen Plattformen und nicht nur im Windows.
Delphi-Quellcode:
type
  IFoo = interface
    ['{05C90FF9-4788-49FB-9C60-CA901F8BBEBA}']
    procedure Foo;
  end;
  IBar = interface(IFoo)
    ['{01B62A8B-40E4-417C-AC63-F787C26DA89F}']
    procedure Bar;
  end;
  IBlubb = interface
    ['{A35EE740-965E-4F97-9AC2-A99F165B0322}']
    procedure Foo;
  end;
  TFooBar = class(TInterfacedObject, IBar, IFoo, IBlubb)
    procedure Bar;
    procedure Foo;
  end;

procedure TForm3.FormCreate(Sender: TObject);
var
  F: IFoo;
  B: IBar;
  X: IBlubb;
begin
  B := TFooBar.Create as IBar;
  F := B;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(B), Pointer(F)]));
  F := B as IFoo;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(B), Pointer(F)]));

  F := TFooBar.Create as IFoo;
  B := F as IBar;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(F), Pointer(B)]));

  X := B as IBlubb;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(F), Pointer(X)]));
end;

procedure TFooBar.Bar;
begin
end;

procedure TFooBar.Foo;
begin
end;
Im Grunde geht es darum, ob bei
Delphi-Quellcode:
procedure Test(Ref: IFoo);
die Interfaces immer die selbe Referenz besitzen
und man dieses "Objekt" somit immer zuverlässig in Listen finden kann oder ob ich doch besser immer die interen Objekt-Referenzen vergleichen muß,
bzw. ob ich zu Beginn immer das Interface geziehlt auf ein Bestimmtes casten (Supports/AS) sollte.

Sir Rufo 6. Mär 2015 00:21

AW: Gleiche Interface-Referenzen?
 
Es ist da doch etwas komplizierter:
Delphi-Quellcode:
program dp_184186;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  IFoo = interface
    ['{E4CA5AB6-B9DF-403C-B30B-72636041B010}']
  end;

  IBar = interface( IFoo )
    ['{6DFEAA0D-E3CA-4DCE-8450-7E54DC6DD7AC}']
  end;

  TFoo = class( TInterfacedObject, IFoo )
  end;

  TBar = class( TInterfacedObject, IBar )
  end;

  TFooBar = class( TFoo, IBar )
  end;

  TFooFooBar = class( TFoo, IFoo, IBar )
  end;

procedure PrintIntfRef( const RefName: string; const Ref: IInterface );
begin
  Writeln( Format( '%s($%p)', [RefName, Pointer( Ref )] ) );
end;

procedure PrintFoo( const AFoo: IFoo );
begin
  PrintIntfRef( 'IFoo', AFoo );
end;

procedure PrintBar( const ABar: IBar );
begin
  PrintIntfRef( 'IBar', ABar );
end;

procedure Test;
var
  LBar: IBar;
begin
  Writeln( 'TBar' );
  LBar := TBar.Create;
  PrintFoo( LBar );
  // PrintFoo( LBar as IFoo ); // EIntfCastError
  PrintBar( LBar );

  Writeln( 'TFooBar' );
  LBar := TFooBar.Create;
  PrintFoo( LBar );
  PrintFoo( LBar as IFoo );
  PrintBar( LBar );

  Writeln( 'TFooBar' );
  LBar := TFooFooBar.Create;
  PrintFoo( LBar );
  PrintFoo( LBar as IFoo );
  PrintBar( LBar );
end;

begin
  try
    Test;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
Und die Lösung lautet hier:
Delphi-Quellcode:
procedure PrintFooAsFoo( const AFoo: IFoo );
var
  LFoo: IFoo;
begin
  if Supports( AFoo, IFoo, LFoo )
  then
    PrintIntfRef( 'IFoo', LFoo )
  else
    PrintIntfRef( 'IFoo', AFoo );
end;

Stevie 6. Mär 2015 07:29

AW: Gleiche Interface-Referenzen?
 
Zitat:

Zitat von himitsu (Beitrag 1292552)
ist es nur Zufall, oder haben abgeleitete Interfaces in den Objekten wirklich "immer" die selben Referenzen?

Für die Interface table nimmt der Compiler hier tatsächlich denselben Slot, soweit in derselben Klasse implementiert, um die Instancesize möglichst gering zu halten.
Ob das auch für die mobilen Plattformen gilt, kann ich dir nicht sagen, aber ist mit folgendem Code einfach herauszufinden:

Delphi-Quellcode:
var
  table: PInterfaceTable;
  entry: PInterfaceEntry;
  i: Integer;
begin
  table := TFooBar.GetInterfaceTable;
  for i := 0 to table.EntryCount - 1 do
    Writeln(GUIDToString(table.Entries[i].IID), ' ', table.Entries[i].IOffset);

himitsu 6. Mär 2015 07:59

AW: Gleiche Interface-Referenzen?
 
Mist, dann werde ich wohl doch eine Typ-Prüfmethode Vorsoglich-Umwandlungsmethode (Supports) einbauen müssen.

Eine Tyüprüfung ala IS (
Delphi-Quellcode:
if not (Value is IFoo) then Value := Value as IFoo;
) geht ja nicht.
Bei Objekten zeigen unterschiedlich gecastete "Typen" ja immer auf die selbe Adresse und für Objekte kenn ich nichts, um den Interface-Typ hinter einem Interface festzustellen, sowas wie
Delphi-Quellcode:
if Value.Type = IFoo
.

Dachte ich kann mir die zusäztlichen Operationen sparen, welche ja wild mit der Referenzzählung und zusätzlichen Variablen rumfuchteln.



Also hier nutzen die schon nicht mehr den selben Slot,
außer man deklariert TFooBar so
Delphi-Quellcode:
class(TFoo, IBar, IFoo, IBlubb)
.

Da die Klasse nur intern ist, kann ich erzwingen, daß es genau so definiert wäre, aber ob der Code dann sicher wäre. :?
Delphi-Quellcode:
type
  IFoo = interface
    ['{05C90FF9-4788-49FB-9C60-CA901F8BBEBA}']
    procedure Foo;
  end;
  IBar = interface(IFoo)
    ['{01B62A8B-40E4-417C-AC63-F787C26DA89F}']
    procedure Bar;
  end;
  IBlubb = interface
    ['{A35EE740-965E-4F97-9AC2-A99F165B0322}']
    procedure Foo;
  end;
  TFoo = class(TInterfacedObject, IFoo)
    procedure Foo;
  end;
  TFooBar = class(TFoo, IBar, IBlubb)
    procedure Bar;
  end;

procedure TForm3.FormCreate(Sender: TObject);
var
  F: IFoo;
  B: IBar;
  X: IBlubb;
begin
  B := TFooBar.Create as IBar;
  F := B;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(B), Pointer(F)]));
  F := B as IFoo;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(B), Pointer(F)]));

  F := TFooBar.Create as IFoo;
  B := F as IBar;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(F), Pointer(B)]));

  X := B as IBlubb;
  Memo1.Lines.Add(Format('$%p $%p', [Pointer(F), Pointer(X)]));
end;

procedure TFooBar.Bar;
begin
end;

procedure TFoo.Foo;
begin
end;

Aber vielleicht lass' ich es erstmal so und bau mir eine Methode, welche am Anfang die InterfaceTable ausslist und prüft. :gruebel:
Müsste ja jedes Interface, was von außerhalb meiner Klasse kommt immer schön prüfen und umwandeln, bevor es in den Listen landet und/oder verglichen wird.

Statt einem
Zitat:

if Value as TFoo = Self then
wollte ich schon ein if Value.IsSelf(Self) then einbauen, aber in mobilen macht das keinen Unterschied, da es in beiden Varianten zusätzliche Variablen mit zusätzlichen Referenzen gibt (intern ja das Self).



Ach ... ich denk nochmal 'nen Tag und dann am Abend gucken, was ich (erstmal) mach.

himitsu 6. Mär 2015 09:21

AW: Gleiche Interface-Referenzen?
 
n'Abend.

Es sind bereits alle Parameter niemals als CONST deklatiert. (Das Problem mit den 0-Referenzen kennen wir nun ja bereits)
Und dann wird es wohl auf eigene Comparer für die Listen hinauslaufen, welche ausschließlich das interne Objekt vergleichen und die Interfaces bleiben so wie sie sind. :stupid:

Schade nur, daß man keine Interface-Methoden als "private" deklarieren kann.
In einem anderen Interface würde es zwar verstecken, aber dann gibt es wieder zusätzliche Casts und Referenzen.
> muß mir nur überlegen, ob ich diese Methoden öffentlich deklariere, oder ob ich das über Objekt-Referenzen löse.

NicoDE 6. Mär 2015 12:31

AW: Gleiche Interface-Referenzen?
 
COM schreibt vor, dass ein Objekt für IUnknown immer den selben Zeiger zurückgeben muss (damit die Identität geprüft werden kann).
Und ich gehe mal davon aus, dass Delphi sich mit seinem IInterface auch daran hält (was nicht für Quellen von dritten gelten muss).

ps: so in etwa (ungetestet)
Delphi-Quellcode:
function CompareInterface(AInterface1, AInterface2: IInterface): Integer;
var
  Lhs: IInterface;
  Rhs: IInterface;
begin
  if Assigned(AInterface1) then
    if Failed(AInterface1.QueryInterface(IInterface, Lhs)) then
      Pointer(Lhs) := nil;
  if Assigned(AInterface2) then
    if Failed(AInterface2.QueryInterface(IInterface, Rhs)) then
      Pointer(Rhs) := nil;
  if PAnsiChar(Lhs) > PAnsiChar(Rhs) then
    Result := 1
  else if PAnsiChar(Lhs) < PAnsiChar(Rhs) then
    Result := -1
  else
    Result := 0;
end;

himitsu 6. Mär 2015 13:26

AW: Gleiche Interface-Referenzen?
 
Ja, bei einem Interface-Typ stimmt das,

aber bezüglich der Interface-Vererbung, sieht das wohl nicht ganz so eindeutig aus. :?





Es ist garnicht so leicht eine sich selbst referenzierende Baumstrucktur, mit zusätzlichen Verlinkungen, ordentlich hinzubekommen, ohne daß sie sich selbst im Speicher hält und ohne daß abhängige Objekte vorzeitig verschwinden. :stupid:
Aber ich glaub langsam hab ich's. (aktuell versuchsweise in einem JSON-DOM implementiert)

Nur den Veruch die Speicher-/Referenzverwaltung in generischen Basisklassen zu implementieren und die dann überall zu verwenden, mußte ich leider aufgeben.
(mit Makros wäre es bestimmt gegangen, aber die Generics sind einfach nur :kotz:)


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