Board index » delphi » Casting Object as Interface (D3)

Casting Object as Interface (D3)

I am trying to cast an object as an interface at runtime in D3, and not
having much
success.

According to the docs with D3, this should work...

...

type
   IDog = interface
      procedure Bark;
   end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
   Dog: IDog;
   AnObject: TInterfacedObject;
begin
   // Cast an Object to be a
   // Dog interfaced object

   Dog := AnObject;            // Doesen't compile
   Dog := AnObject as IDog;    // Doesen't compile
   Dog := IDog(AnObject);      // Doesen't compile
end;
...

Any thoughts?

Mike

 

Re:Casting Object as Interface (D3)


mi...@gte.net wrote in article <5kljgl$kp...@news10.gte.net>...

Quote
> I am trying to cast an object as an interface at runtime in D3, and not
> having much
> success.
> ...

> type
>    IDog = interface
>       procedure Bark;
>    end;

> .....
> var
>    Dog: IDog;
>    AnObject: TInterfacedObject;
> begin
>    // Cast an Object to be a
>    // Dog interfaced object

>    Dog := AnObject;            // Doesen't compile
>    Dog := AnObject as IDog;    // Doesen't compile
>    Dog := IDog(AnObject);      // Doesen't compile
> end;

The problem is caused by the fact that AnObject is a TInterfacedObject, not
a class derived from TInterfacedObject.  TInterfacedObject doesn't actually
have the ability to retrieve interfaces on itself, until you introduce at
least one interface into the class, therefore the first assignment and
third assignments won't work.  

The interface must have a GUID, else you can't QueryInterface for it.
Therefore, the second assignment will fail.  If you fix the first problem
but don't fix the GUID, you'll get a seemingly strange situation where the
second assignment gives an error, but the third one doesn't.  This is
because the form IDog(AnObject) is a compile-time resolved cast, which the
compiler can accomplish without doing a QueryInterface.  The "AnObject as
IDog" form tells the compiler to use QueryInterface, which means the
interface *must* have a GUID.

The following unit, which is derived from yours, compiles correctly.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComObj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
type
   IDog = interface
     ['{b9c6bf80-c575-11d0-a6df-444553540000}']
      procedure Bark;
   end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

type
   TDog = class(TInterfacedObject, IDog)
      procedure Bark;
   end;
procedure TForm1.Button1Click(Sender: TObject);
var
   Dog: IDog;
   AnObject: TDog;
begin
   // Cast an Object to be a
   // Dog interfaced object

   Dog := AnObject;            // Doesen't compile
   Dog := AnObject as IDog;    // Doesen't compile
   Dog := IDog(AnObject);      // Doesen't compile
end;

procedure TDog.Bark;
begin
  MessageDlg('Woof', mtInformation, [mbOk], 0);
end;

end.

Re:Casting Object as Interface (D3)


Quote
In article <5kljgl$kp...@news10.gte.net>, mi...@gte.net wrote:
>I am trying to cast an object as an interface at runtime in D3, and not
>having much
>success.
>According to the docs with D3, this should work...
>....
>type
>   IDog = interface
>      procedure Bark;
>   end;

 ..rest snipped

Umm, I know you may have missed this part in your example, but I don't
think your example will work unless TInterfacedObject is included as
part of the TInterfacedObject = class(...) definition.  Interfaces
don't let you overlay foreign interfaces on classes that haven't okayed
them :)

  --=- Ritchie Annand

Re:Casting Object as Interface (D3)


Conrad,
Thank you for your helpful message.  The part about needing a GUID I was
unaware of.

Let me be a little more clear.  I want to exploit the polymorphic nature
of interfaces.  I am implementing an Explorer-like interface and I want
to be able to iterate through a TList, casting each member as having the
IExplorable interface that I have created.

To be more specific, I want to iterate through the nodes of a tree and
tell
the data in the node, which is an object, to display itself, or edit
itself,
or whatever.

Something like:

with Node.Data as IExplorable do
   begin
      Node.Data.DoSomething;
      {etc...}
   end;

I am *so close* to making this very powerful mechanism work.  Delphi's
docs
go on and on about how powerful polymorphism in interfaces is, but
provides
no good examples!

So, the crux of the matter is that at compile time I have no idea what
the
type of the objects in the list are, so at runtime I need to cast them
as IExplorable because I know that whatever the true type of the object
it
will always implement the IExplorable interface.

So, how do you do that?

Mike

Quote
Conrad Herrmann wrote:
> The problem is caused by the fact that AnObject is a TInterfacedObject, not
> a class derived from TInterfacedObject.  TInterfacedObject doesn't actually
> have the ability to retrieve interfaces on itself, until you introduce at
> least one interface into the class, therefore the first assignment and
> third assignments won't work.

> The interface must have a GUID, else you can't QueryInterface for it.
> Therefore, the second assignment will fail.  If you fix the first problem
> but don't fix the GUID, you'll get a seemingly strange situation where the
> second assignment gives an error, but the third one doesn't.  This is
> because the form IDog(AnObject) is a compile-time resolved cast, which the
> compiler can accomplish without doing a QueryInterface.  The "AnObject as
> IDog" form tells the compiler to use QueryInterface, which means the
> interface *must* have a GUID.

> The following unit, which is derived from yours, compiles correctly.

> unit Unit1;

> interface

> uses
>   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
>   StdCtrls, ComObj;

> type
>   TForm1 = class(TForm)
>     Button1: TButton;
>     procedure Button1Click(Sender: TObject);
>   private
>     { Private declarations }
>   public
>     { Public declarations }
>   end;
> type
>    IDog = interface
>      ['{b9c6bf80-c575-11d0-a6df-444553540000}']
>       procedure Bark;
>    end;

> var
>   Form1: TForm1;

> implementation

> {$R *.DFM}

> type
>    TDog = class(TInterfacedObject, IDog)
>       procedure Bark;
>    end;
> procedure TForm1.Button1Click(Sender: TObject);
> var
>    Dog: IDog;
>    AnObject: TDog;
> begin
>    // Cast an Object to be a
>    // Dog interfaced object

>    Dog := AnObject;            // Doesen't compile
>    Dog := AnObject as IDog;    // Doesen't compile
>    Dog := IDog(AnObject);      // Doesen't compile
> end;

> procedure TDog.Bark;
> begin
>   MessageDlg('Woof', mtInformation, [mbOk], 0);
> end;

> end.

Re:Casting Object as Interface (D3)


On 5 May 1997 21:29:57 GMT, mi...@gte.net wrote:

Quote
>type
>   IDog = interface
>      procedure Bark;
>   end;

In order to use QueryInterface (which is how the AS operator works for
an interface), the interface must have a GUID, e.g.,

type
  IDog = interface
    [{'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}']
    procedure Bark;
  end;

Quote
>var
>   Dog: IDog;
>   AnObject: TInterfacedObject;
>begin
>   Dog := AnObject;            // Doesen't compile

You can assign AnObject to Dog only if the static type of AnObject
inherits from that static type of Dog.

Quote
>   Dog := AnObject as IDog;    // Doesen't compile

Because the IDog interface does not have a GUID, Delphi cannot call
QueryInterface.

Quote
>   Dog := IDog(AnObject);      // Doesen't compile

You can cast AnObject to IDog this way only if the static type of
AnObject implements the IDog interface.

It will take some time to grow accustomed to interfaces. It's a new
way of working in Delphi, but think of the alternative. It's much
easier in Delphi than in any other language.
--
Ray Lischner            
Author of Secrets of Delphi 2 (http://www.tempest-sw.com/secrets/)

Re:Casting Object as Interface (D3)


Thank you for your helpful message.  The part about needing a GUID I was
unaware of.

Let me be a little more clear.  I want to exploit the polymorphic nature
of interfaces.  I am implementing an Explorer-like interface and I want
to be able to iterate through a TList, casting each member as having the
IExplorable interface that I have created.

To be more specific, I want to iterate through the nodes of a tree and
tell the data in the node, which is an object, to display itself, or
edit itself,
or whatever.

Something like:

with Node.Data as IExplorable do
   begin
      Node.Data.DoSomething;
      {etc...}
   end;

I am *so close* to making this very powerful mechanism work.  Delphi's
docs go on and on about how powerful polymorphism in interfaces is, but
provides no good examples!

So, the crux of the matter is that at compile time I have no idea what
the type of the objects in the list are, so at runtime I need to cast
them
as IExplorable because I know that whatever the true type of the object
it
will always implement the IExplorable interface.

So, how do you do that?

Mike

Quote
Ray Lischner wrote:

> On 5 May 1997 21:29:57 GMT, mi...@gte.net wrote:

> >type
> >   IDog = interface
> >      procedure Bark;
> >   end;

> In order to use QueryInterface (which is how the AS operator works for
> an interface), the interface must have a GUID, e.g.,

> type
>   IDog = interface
>     [{'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}']
>     procedure Bark;
>   end;

> >var
> >   Dog: IDog;
> >   AnObject: TInterfacedObject;
> >begin
> >   Dog := AnObject;            // Doesen't compile
> You can assign AnObject to Dog only if the static type of AnObject
> inherits from that static type of Dog.

> >   Dog := AnObject as IDog;    // Doesen't compile
> Because the IDog interface does not have a GUID, Delphi cannot call
> QueryInterface.

> >   Dog := IDog(AnObject);      // Doesen't compile
> You can cast AnObject to IDog this way only if the static type of
> AnObject implements the IDog interface.

> It will take some time to grow accustomed to interfaces. It's a new
> way of working in Delphi, but think of the alternative. It's much
> easier in Delphi than in any other language.
> --
> Ray Lischner
> Author of Secrets of Delphi 2 (http://www.tempest-sw.com/secrets/)

Re:Casting Object as Interface (D3)


Well, it looks as though I figured it out.

Here it is:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  IPushable = interface
  ['{b9c6bf80-c575-11d0-a6df-444553540000}']}
  procedure PushMe;
  end;

type
  TPushable = class(TInterfacedObject, IPushable)
  procedure PushMe; virtual;
  end;

type
  TPushableSubclass = class(TPushable)
  procedure PushMe; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
        a: Integer;
        MyList: TList;
        MyPushable: TPushable;
        MyPushableSubclass: TPushableSubclass;
begin
  // Add an object that implements the interface
  // to a TList object.
  MyPushableSubclass := TPushableSubclass.Create;
  MyList := TList.Create;
  a:= MyList.Add(MyPushableSubclass);

  // Downcast the Object to the base class that
  // implements the interface
  MyPushable := TPushable(MyList.Items[0]);

  // Invoke the interface implemented methods
  with MyPushable as IPushable do begin
    // PushMe is invoked in the subclass
    // because the method is virtual
    MyPushable.PushMe;
  end;
end;

procedure TPushable.PushMe;
begin
        ShowMessage('Hey, stop pushing!');
end;

procedure TPushableSubclass.PushMe;
begin
        ShowMessage('Pushed!');
end;

end.

Re:Casting Object as Interface (D3)


Mike <a...@gte.net> wrote in article <5kn4gs$dk...@news8.gte.net>...

Quote
> Let me be a little more clear.  I want to exploit the polymorphic nature
> of interfaces.  I am implementing an Explorer-like interface and I want
> to be able to iterate through a TList, casting each member as having the
> IExplorable interface that I have created.

> To be more specific, I want to iterate through the nodes of a tree and
> tell the data in the node, which is an object, to display itself, or
> edit itself,
> or whatever.

> Something like:

> with Node.Data as IExplorable do
>    begin
>       Node.Data.DoSomething;
>       {etc...}
>    end;

> I am *so close* to making this very powerful mechanism work.  Delphi's
> docs go on and on about how powerful polymorphism in interfaces is, but
> provides no good examples!

Not having D3, my suggestion is hypothetical...

From the container (a tree in this case?), derive a container
specific to the Base class of the objects you wish to insert, say,
TFooTree.  Override (hide, actually) the Add and ItemIs methods
to look like

proc TFooTree.Add(const aFoo: TFoo);
begin
  inherited Add(aFoo);
end;

func TFooTree.ItemIs(const index: integer): TFoo;
begin
  Result := TFoo(Items[index]);
end;

This gives you type safety at compile time, and the casts are all
guaranteed correct and safe.

Provide a Display; virtual; for TFoo and all its derivatives.

Finally (well, close to it), add a DisplayAll to TFooTree:

var f: TFoo;
for i := 0 to endoftree do begin // or whatever the range is
  f := ItemIs(i);
  f.Display;
end;

or something like that.
--
Grace + Peace   *   Peter N Roth  *   Engineering Objects International
Author of "C++ Jump Start" ISBN 0-9655862-2-7.
Tools for Developers: ClassBuilder 4 for Delphi, ClassBuilder++ for C++
Visit our website at http://www.inconresearch.com/eoi

Re:Casting Object as Interface (D3)


mi...@gte.net wrote in article <5knd4m$6n...@news2.gte.net>...

Quote
> Well, it looks as though I figured it out.
> type
>   TPushable = class(TInterfacedObject, IPushable)
>   procedure PushMe; virtual;
>   end;

> type
>   TPushableSubclass = class(TPushable)
>   procedure PushMe; override;
>   end;

This doesn't do much to demonstrate the real power of polymorphism through
interfaces, since you're not really using the interface to implement
polymorphism, but instead using a virtual method.  (Still, you do get other
advantages from COM.)

A demonstration of polymorphism through interfaces would include making a
class that is completely unrelated  (through derivation) to TPushable but
which implements IPushable.  For example:

type
  TDoor = class(TInterfacedObject, IPushable)
    procedure PushMe;
    //...other methods and properties that represent doors
  end;

  TRock = class(TInterfacedObject, IPushable)
    procedure PushMe;
    // ...other methods and properties that represent rocks
  end;

Given an IUnknown pointer u, which you know points to either a door or a
rock, you can do this:

procedure PushTheObject( u: IUnknown);
begin
  (u as IPushable).PushMe;
end;

One difference between the TPushable/TPushableSubclass example and the
TDoor/TRock example is that the class names you use for the latter are more
concrete because they're derived from the objects themselves.  This maps
cleanly onto human languages: the classes TDoor and TRock are nouns, the
interface (IPushable) is an adjective.

-- Conrad Herrmann

COM-based strangers meeting in Switzerland:
A: B.QI(IGerman)?
B: E_NOTIMPL
A: B.QI(IFrench)?
B: S_OK.
B: A.QI(IItalian)?
A: E_NOTIMPL
B: A.QI(IEnglish)?
A: S_OK
B: A.QI(IRomanche)?
A: E_NOTIMPL
B: (A as IEnglish).Say("Hello");
A: (B as IFrench).Dit("Salut");

Re:Casting Object as Interface (D3)


Cool.  It's like multiple inheritance without dragging along unneeded
data and procedures, right?

Conrad Herrmann <cherrman-nojunkma...@ix.netcom.com> wrote in article
<01bc5a48$48c5d0c0$ece31fcc@bohr>...

Quote
> mi...@gte.net wrote in article <5knd4m$6n...@news2.gte.net>...
> A demonstration of polymorphism through interfaces would include making a
> class that is completely unrelated  (through derivation) to TPushable but
> which implements IPushable.  For example:

> type
>   TDoor = class(TInterfacedObject, IPushable)
>     procedure PushMe;
>     //...other methods and properties that represent doors
>   end;

>   TRock = class(TInterfacedObject, IPushable)
>     procedure PushMe;
>     // ...other methods and properties that represent rocks
>   end;

> Given an IUnknown pointer u, which you know points to either a door or a
> rock, you can do this:

> procedure PushTheObject( u: IUnknown);
> begin
>   (u as IPushable).PushMe;
> end;

Re:Casting Object as Interface (D3)


Quote
> Conrad Herrmann <cherrman-nojunkma...@ix.netcom.com> wrote in article
> <01bc5a48$48c5d0c0$ece31fcc@bohr>...
> > mi...@gte.net wrote in article <5knd4m$6n...@news2.gte.net>...
> > A demonstration of polymorphism through interfaces would include making
a
> > class that is completely unrelated  (through derivation) to TPushable
but
> > which implements IPushable.  

Keith G. Murphy <keith...@concentric.net> wrote in article
<01bc5b01$b9353c60$656464c0@keithmur>...

Quote
> Cool.  It's like multiple inheritance without dragging along unneeded
> data and procedures, right?

It depends on what you mean by multiple inheritance.  In C++, MI is used to
implement interfaces, so there's obviously a similarity there.  However,
only in Mike's example is the actual code for Push shared.  In my example,
while both the TRock and TDoor can be pushed, each Push method has to be
implemented separately.

-- Conrad Herrmann

Other Threads