Solved

More interface woes (Acessing original object methods through an interface)

Posted on 2003-10-27
10
207 Views
Last Modified: 2010-04-05
Having come to the Delphi world via VB, I presumed that the following would be possible:

procedure bar( fooi: IFoo);
var
  foot : TFoo;
begin
  foot := TFoo(fooi);
  MessageBox(0,pchar(foot.str),'',1);
end;

var
  foot : TFoo;
begin
  foot := TFoo.create;
  foot.str := ('hallo');
  bar(foot);
end.

i.e. That after storing a TFoo in an IFoo that I could cast IFoo back to type TFoo. Have I done something stupid? How can I perform such a cast?
0
Comment
Question by:davelane
  • 4
  • 4
  • 2
10 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 9627250
Yep. Something stupid... But hey, we've all done stupid things in the past... ;-)

And no, a class type is not compatible with an interface. Even worse, you could have a class like this:

type TBar = class(TInterfacedObject, IFoo);

Now, the class would be TBar so what do you think would happen if you assign a TBar to a TFoo? These could be incompatible types...

An interface is nothing more than a definition of methods and where they can be found inside an object. Since multiple components can support the same interface, you're never sure that the interface is pointing to the right class. Which is why it's near-impossible to typecast an interface back to a class.

Can it be done? Sure, but the complexity is a bit much. Besides, it's a bit risky if the interface isn't a Delphi interface but created in some other application. If it is a Delphi interface then the interface is pointing to a class that is inherited from TObject. And TObject can tell you the exact class of the object. However, to get to the TObject from an interface??? I don't know exactly how to do that.
0
 

Author Comment

by:davelane
ID: 9628357
Well when I meant stupid, I meant obvious :-( In VB this is possible (because even classes have hidden interfaces). And anyway my point is that this is a reasonable thing to want to do. Imagine a unit where I define 2 objects

  I1 = interface
    function DoSomething: integer;
  end;

  T1 = class(TIntefacedObject, I1)
  private
    InternalData: string;
    function DoSomethingInternal: integer;
  public
    function DoSomething: integer;
  end;

  T2 = class(TInterfacedObject)
  public
    function DoSomethingToAT1(T1Object: I1): integer;
  end;

In function DoSomethingToAT1 I want to be able to access the private functions and data on the object but I cant do this because they are not defined on the interface.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 9628842
Unfortunately, what you are trying is just not allowed. You use an interface to make the object more abstract and once it's more abstract you can't undo it anymore. If your interface doesn't provide access to any private methods, you just don't have any access to them. Now, with this definition:

  T2 = class(TInterfacedObject)
  public
    function DoSomethingToAT1(T1Object: T1): integer;
  end;

Notice I use T1 instead of I1. This way you will get access to the private methods. Unfortunately this doesn't allow you to send the object over through it's interface.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Expert Comment

by:j42
ID: 9632163
Hi davelane,

IM(H)O your should rethink about your design. If function DoSomethingToAT1(T1Object: I1) needs acess to private members of T1 then they are connected in some way. So decoupling (and as far as I understand that is what interfaces are for) is not neccessary and not possible. You could pass a T1 object to it - as Workshop_Alex said.
0
 

Author Comment

by:davelane
ID: 9632198
Thanks for the replies guys,
like I said I came from VB, where this is allowed and oft used. Have been using Delphi for 2 years now, but have only now started using Interfaces under Delphi. Like I said I (still) don't think it is an unreasonable thing to want to do. Decoupling is one of the things that interfaces facilitate, but to say they are only for decoupled object is wrong IMHO. And anyway in my case the objects are not couipled. Take this example:
2 objects with interfaces IDatabaseTable and IRow. IDatabaseTable is basically a collection of IRows. Now say I have a function IDatabaseTable.Append(IRow). It is clear that this append function will want to do things to IRow that are not on the public interface. What would be good design in this case? A hidden interface with the necessary functions: IRowInternal?
0
 
LVL 2

Expert Comment

by:j42
ID: 9632718
Well... I have never been into VB and database programming.

> public interface
I guess in  Delphi there is nothing like a 'hidden' or private interface.

> IDatabaseTable is basically a collection of IRows
I do not like that (maybe because interfaces are slightly different in VB). I would say:
TDatabaseTable (an implementation of IDatabaseTable) is a collection of TRow s which implement IRow.

What if there is something like IDatabaseTable.Append(IRow) and you 'cast' the IRow to a TRow. I can implement another class
with an IRow interface but completely different internals. Thus Append(IRow) will crash if I pass my object to it.
As far as I understand your problem you need a certain behaviour of your rows, so why not put it in the interface? Or something like
TRow = class(TWhatEver, IRow, IForInternalManipulation);

> but to say they are only for decoupled object is wrong
I am not a native english speaker so please do not take me to literal ;-)
0
 

Author Comment

by:davelane
ID: 9632771
> TDatabaseTable (an implementation of IDatabaseTable) is a collection of TRow s which implement IRow.
You say don't take me too literally, but that is obviously what I meant. :-)

> I guess in  Delphi there is nothing like a 'hidden' or private interface.
Well by hidden I meant, not to include it in the .pas file that defines the public interfaces and / or not to include it in the tlb if and when it exists. TRow = class(TWhatEver, IRow, IForInternalManipulation); is exactly what I meant.

> What if there is something like IDatabaseTable.Append(IRow) and you 'cast' the IRow to a TRow. I can
> implement another class with an IRow interface but completely different internals. Thus Append(IRow)
> will crash if I pass my object to it.
Right, I will have to think about it though. The main reason for using interfaces in this case is to provide some kind of memory management.
0
 
LVL 2

Accepted Solution

by:
j42 earned 125 total points
ID: 9633709
> in this case is to provide some kind of memory management.
Ok, another try (beside the whole communication stuff):

IRowWrapper = interface
  function GetRow: TRow;
end;

procedure IDatabaseTable.Append(ARow: IRowWrapper);
var
  row: TRow;
begin
  row := ARow.GetRow;
  row.AccesSomeVeryPrivateAndInSomeWayHiddenDataOfRow;
  ...
end;

This way you have reference counting, access to internals and no ugly casts.
0
 

Author Comment

by:davelane
ID: 9633836
Yeah, I've already done something similar (couldn't do exactly what you said due to a problem with circular references).

IRow = interface
  function GetClassPointer: Pointer;
end;

function TRow.GetClassPointer: Pointer;
begin
  result := Self;
end;

procedure IDatabaseTable.Append(ARow: IRow);
var
  row: TRow;
begin
  row := TRow(ARow.GetClassPointer);
  row.AccesSomeVeryPrivateAndInSomeWayHiddenDataOfRow;
  ...
end;

It ain't pretty but it works. Thanks for the help.
0
 
LVL 2

Expert Comment

by:j42
ID: 9634181
I'm glad to be helpful!
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

733 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question