If i have these 3 interfaces
unit Interfaces;
interface
type
IA = interface['{86367399-1601-4FDD-89CF-362E762D948E}']
procedure doSomething;
end;
IB = interface['{070B9364-4A57-4E5A-BBA7-FBF1C6A48C5A}']
procedure doSomething;
end;
IC =interface(IB)['{DAC8021C-42CB-40EC-A001-466909044EC3}']
procedure doSomething;
end;
implementation
end.
How do i resolve naming conficts in a class that implements IA and IC?
I have no problem with IA and IC but how do i implement IB?
type
myClass = class(TInterfacedObject, IA, IC)
procedure doASomething;
procedure doBSomething;
procedure doCSomething;
procedure IA.doSomething = doASomething;
// ??? = doBSomething;
procedure IC.doSomething = doCSomething;
end;
Your class doesn't implement IB. So there's no way to use a method resolution clause for an interface that is not implemented by that class. So, you might imagine that the easy way out is to implement that interface:
type
myClass = class(TInterfacedObject, IA, IB, IC)
procedure doASomething;
procedure doBSomething;
procedure doCSomething;
procedure IA.doSomething = doASomething;
procedure IB.doSomething = doBSomething;
procedure IC.doSomething = doCSomething;
end;
But this fails to compile also. The error is:
[dcc32 Error]: E2291 Missing implementation of interface method IB.doSomething
I conclude that there is no way for you to implement these interfaces directly. I think that the only way you could square this particular circle would be to use an implements clause to delegate implementation. For instance:
type
myClass = class(TInterfacedObject, IA, IB, IC)
private
FA: IA;
FB: IB;
FC: IC;
property A: IA read FA implements IA;
property B: IB read FB implements IB;
property C: IC read FC implements IC;
end;
Obviously you'd need to initialise the fields FA, FB and FC, but I leave that as an exercise for you.
This isn't a very satisfactory solution. If I were in your place, I'd be looking very hard for a solution that avoided the name clashes in the first place.
As per my comment:
Does it really make sense for an extension of IB (namely IC) to redeclare an identical method? Since IC extends IB, procedure doSomething; is already available. As a client using an implementation of IC, how would you call IC's doSomething differently from IB's doSomething?
So if you do the following, you should have no trouble.
IC =interface(IB)['{DAC8021C-42CB-40EC-A001-466909044EC3}']
//procedure doSomething; //Don't redeclare an existing method in an extension.
end;
type
myClass = class(TInterfacedObject,
//Explicitly state that you're implementing IB
IA, IB, IC)
procedure doASomething;
procedure doBSomething;
procedure doCSomething;
procedure IA.doSomething = doASomething;
//And you'll be able to define IB's method resolution.
procedure IB.doSomething = doBSomething;
procedure IC.doSomething = doCSomething;
end;
You may also find some of the information in this question and answers useful, as it discusses why you need to explicitly state which interfaces a class implements.
Related
I want to create an Interfaced object that supports an Interface from somewhere else + my own functions. So, how to stack/aggregate/enhance the Interface? I guess its possible, but I cant find a snippet or demo specific for my inheritance experiment.
This solution is not quite what I want:
TImplement = Class(TInterfacedObject, IOne, ITwo)
private
FOne: IOne;
public
property One: IOne read FOne implements IOne;
property Two: ITwo read FTwo implements ITwo;
end;
Current usage:
(MyInterface as IOne).Something;
(MyInterface as ITwo).SomethingElse;
Desired usage:
MyInterface.Something;
MyInterface.SomethingElse;
I tried inheriting the Interface:
ITogether = Interface(IOne)
procedure SomeThingElse;
end:
TImplement = Class(TInterfacedObject, ITogether)
// or Class(TInterfacedObject, ITogether, IOne) => Both result in missing Implementation message on compile ...
private
FOne: IOne;
function SomeThingElse;
public
property One: IOne read FOne implements IOne;
end;
This combination says something like:
E2291 Implementation of Method x From Interface IOne missing.
Is it possible to combine the Interface in a way so that the "cast free" calls are possible?
Edit:
Rob Lambden´s answer is for me the missing Information. Uwe Raabes Answer is the Correct implementation. (And probably the only one possible)
So uwe wins the answer and i can only upvote Robs answer.
You can implement the IOne methods and forward them to the FOne interface.
type
IOne = interface
['{19F785C0-5D2E-479F-BB2C-88A00BA4C812}']
procedure Something;
end;
ITogether = interface(IOne)
['{B8B7F690-DC98-41AB-A6D9-29F70330EDA5}']
procedure SomethingElse;
end;
type
TTogether = class(TInterfacedObject, ITogether)
private
FOne: IOne;
protected
property One: IOne read FOne;
public
constructor Create(AOne: IOne);
procedure SomethingElse;
procedure Something;
end;
constructor TTogether.Create(AOne: IOne);
begin
inherited Create;
FOne := AOne;
end;
procedure TTogether.Something;
begin
One.Something;
end;
procedure TTogether.SomethingElse;
begin
{ Do something else }
end;
AFAIK, there is no language construct like implements that does that for you when the implementor is an interface property.
Update:
In case you have several cases where you need to extend the IOne interface, you can write a wrapper class that in turn makes a good candidate for the implements keyword.
type
TOneWrapper = class
private
FOne: IOne;
protected
property One: IOne read FOne;
public
constructor Create(AOne: IOne);
procedure Something;
end;
type
TTogether = class(TInterfacedObject, ITogether)
private
FOne: TOneWrapper;
protected
property One: TOneWrapper read FOne implements ITogether;
public
constructor Create(AOne: IOne);
destructor Destroy; override;
procedure SomethingElse;
end;
constructor TTogether.Create(AOne: IOne);
begin
inherited Create;
FOne := TOneWrapper.Create(AOne);
end;
destructor TTogether.Destroy;
begin
FOne.Free;
inherited Destroy;
end;
procedure TTogether.SomethingElse;
begin
{ Do something else }
end;
constructor TOneWrapper.Create(AOne: IOne);
begin
inherited Create;
FOne := AOne;
end;
procedure TOneWrapper.Something;
begin
One.Something;
end;
Your question appears to be about two things. Firstly it's about calling the methods without having to cast.
Just use the object reference and you can do exactly that.
MyObject:=TImplements.Create;
MyObject.Something;
MyObject.SomethingElse;
Secondly it's about implementing an interface without having to re-implement the functions.
Delphi Interfaces, by their definition, cannot include implementations. (The methods have to be abstract, or in C++ terms they are 'pure virtual').
This means that you cannot do a multiple-inheritance type implementation like you can with C++. Any object implementing an interface must implement all of the implementing functions ... or ...
You can delegate an interface to a property as in your example, and if you do that you can still call the methods without casting if you use the object reference.
suppose i have this definitions:
TMyClass1 = class
end;
TMyClass2 = class
end;
IModel<T : class> = interface
['{E8262D6C-DCAB-46AC-822E-EC369CF734F8}']
function List() : TObjectList<T>;
end;
IPresenter<T : class> = interface
['{98FB7751-D75A-4C51-B55A-0E5FE68BE213}']
function Retrieve() : TObjectList<T>;
end;
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
TModel<T : class> = class(TInterfacedObject, IModel<T>)
public
function List() : TObjectList<T>; virtual; abstract;
end;
TPresenter<T : class> = class(TInterfacedObject, IPresenter<T>)
strict private
{ Private declarations }
FModel : IModel<T>;
FView : IView<T>;
public
constructor Create(AView : IView<T>);
function Retrieve() : TObjectList<T>; virtual; abstract;
end;
TModelClass1 = class(TModel<TMyClass1>);
TPresenterClass1 = class(TPresenter<TMyClass1>);
TModelClass2 = class(TModel<TMyClass2>);
TPresenterClass2 = class(TPresenter<TMyClass2>);
and i have this form that implements some of the things i defined:
TForm1 = class(TForm, IView<TMyClass1>, IView<TMyClass2>)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FPresenter1 : IPresenter<TMyClass1>;
FPresenter2 : IPresenter<TMyClass2>;
procedure AssignPresenter(APresenter : IPresenter<TMyClass1>); overload;
procedure AssignPresenter(APresenter : IPresenter<TMyClass2>); overload;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass1>);
begin
Self.FPresenter1 := APresenter;
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass2>);
begin
Self.FPresenter2 := APresenter;
end;
so the problem here is that delphi can't figure out what method to call, in this example, only the AssignPresenter(APresenter: IPresenter<TMyClass2>) is called in both cases, so probably im missing something here but i can't figure out atm.
thx in advance.
This is probably a duplicate but I cannot find it right now.
The problem is that the as operator for interfaces is not very compatible with generics. The as operator relies on the interface GUID. The interface is found by querying for an interface with matching GUID. And GUIDs don't fit at all well with generic instantiation.
Now let's look at your code.
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
The problem is that IView<TMyClass1> and IView<TMyClass2> have the same GUID:
type
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
So both IView<TMyClass1> and IView<TMyClass2> share the same GUID, and when you query using as, the same interface will be returned irrespective of whether or not you asked for IView<TMyClass1> or IView<TMyClass2>.
So, the bottom line here is that as is rendered next to useless with generic interfaces as soon as an object implements ISomeInterface<T> twice with different T.
Embarcadero really should implement as in a manner that supports generics. I wouldn't hold your breath though.
You will need to find a different way to solve your problem.
No need to use as when you have the object implementing the interface - just write:
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create(Self);
TPresenterClass2.Create(Self);
end;
The compiler figures this out properly. When you are using as it does a Supports call which fails for the reason explained by David already.
As an addition to David Heffernan's answer.
One way you could work around the problem would be to give a unique GUID to all the generic declarations you use:
IViewMyclass1 = interface(IView<TMyClass1>)
['{1A0F941F-BAB1-4723-A6C1-27036DF5D344}']
end;
IViewMyclass2 = interface(IView<TMyClass2>)
['{0C61A23A-DC50-43B0-97C9-8B0013DDC193}']
end;
Redefine the view's declaration.
TForm4 = class(TForm, IViewMyclass1, IViewMyclass2)
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IViewMyclass1));
TPresenterClass2.Create((Self as IViewMyclass2));
end;
And then the right overload is called.
Disclaimers:
This is the result of On-The-Fly testing (XE4). Reliability is unknown.
I'm aware this isn't very practical.
It's probably not something that should be extensively used as it isn't obvious where the code would fail.
I'm doing a full rewrite of an old library, and I'm not sure how to handle this situation (for the sake of being understood, all hail the bike analogy):
I have the following classes:
TBike - the bike itself
TBikeWheel - one of the bike's wheel
TBikeWheelFront and TBikeWheelBack, both inherits from TBikeWheel and then implements the specific stuff they need on top of it
This is pretty straightforward, but now I decide to create multiple kind of bikes, each bikes having it's own kinds of wheel - they do the same stuff as a regular front/back wheels, plus the specific for that bike.
TBikeXYZ - inherits from TBike
TBikeWheelXYZ - inherits from TBikeWheel
And here is my problem: TBikeWheelFrontXYZ should inherit from TBikeWheelXYZ (to get the specific methods of an XYZ wheel), but it should also inherit from TBikeWheelFront (to get the specific methods of a front wheel).
My question here is, how can I implement that in a way that doesn't:
feel like a hack
force me to rewrite the same code several time
Delphi does not support Multiple Inheritance. But classes can support / implement multiple interfaces and you can delegate interface implementation, so you can kinda simulate multiple inheritence.
Use interfaces. Something like this (Off the top of my head, based on your description.....)
type
IBikeWheel = interface
...
end;
IXYZ = interface
...
end;
IFrontWheel = interface(IBikeWheel)
...
end;
TBike = class
...
end;
TBikeWheel = class(TObject, IBikeWheel);
TBikeWheelXYZ = class(TBikeWheel, IXYZ);
TBikeFrontWheelXYZ = class(TBikeWheelXYZ, IFrontWheel);
Then implement classes for the interfaces that do what the corresponding classes in your old (presumably C/C++) library does and instantiate them in the corresponding class's constructor.
Use polymorhism to implment each 'thing' as an object hierarchy in its own right and then add object properties to that object in turn. So, create a hierarchy of wheels, and a hierarchy of bikes. Then add wheels to bikes as fields in the ancestor bike object. See below.
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
property FrontWheel : TBikeWheel
read FrontWhell
TBikeABC = class( TBike)
constructor Create;
end;
constructor TBikeABC.Create;
begin
inherited;
FFrontWheel := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
constructor Create;
end;
constructor TBikeXYZ.Create;
begin
inherited;
FFrontWheel := TBikeWheelXYZ.Create;
end;
A variation of Brian Frost's suggestion:
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
protected
function CreateWheel: TBikeWheel; virtual;
public
property FrontWheel : TBikeWheel
read FrontWheel
end;
TBikeABC = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeABC.CreateWheel: TBikeWheel;
begin
result := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeXYZ.CreateWheel: TBikeWheel;
begin
result := TBikeWheelXYZ.Create;
end;
Basically - you CAN'T. Delphi does not support multiple inheritance.
So left with that dilemma, the question is: could you possibly refactor that library in such a way that you can get away with using interface? Is the multiple inheritance mostly about functions and methods? If so - use interfaces. Delphi can support multiple interfaces on a class.
If the multi-inheritance is more about inheriting actual functionality in the classes, then you're probably looking at a bigger scale refactoring, I'm afraid. You'll need to find a way to break up those functional dependencies in such a way you can make it inherit from a single base class, possibly with some additional interfaces thrown in.
Sorry I can't provide an easy answer - that's just the reality of it.
Marc
You can try to extract an interface, say IFrontWheel, out of TBikeWheelFront, so that it is a subclass of TBikeWheel but implements IFrontWheel. Then TBikeWheelXYZ inherits from TBikeWheel and TBikeWheelFrontXYZ inherits from TBikeWheelXYZ and implements IFrontWheel.
Then you can define a class TFrontwheel and give it the same methods as the interface, but now you implement them. Then TBikeWheelFront and TBikeWheelXYZ get a private member of type TFrontwheel and the IFrontWheel implementations of them simply delegate to the private member methods.
This way you don't have double implementations.
Another alternative with newer versions of Delphi is to leverage generics in a compositional model. This is particularly useful in the case where the multiple base classes (TBarA and TBarB in this example) are not accessible for modification (ie: framework or library classes). For example (note, the necessary destructor in TFoo<T> is omitted here for brevity) :
program Project1;
uses SysUtils;
{$APPTYPE CONSOLE}
type
TFooAncestor = class
procedure HiThere; virtual; abstract;
end;
TBarA = class(TFooAncestor)
procedure HiThere; override;
end;
TBarB = class(TFooAncestor)
procedure HiThere; override;
end;
TFoo<T: TFooAncestor, constructor> = class
private
FFooAncestor: T;
public
constructor Create;
property SomeBar : T read FFooAncestor write FFooAncestor;
end;
procedure TBarA.HiThere;
begin
WriteLn('Hi from A');
end;
procedure TBarB.HiThere;
begin
WriteLn('Hi from B');
end;
constructor TFoo<T>.Create;
begin
inherited;
FFooAncestor := T.Create;
end;
var
FooA : TFoo<TBarA>;
FooB : TFoo<TBarB>;
begin
FooA := TFoo<TBarA>.Create;
FooB := TFoo<TBarB>.Create;
FooA.SomeBar.HiThere;
FooB.SomeBar.HiThere;
ReadLn;
end.
you can try this way, if you do not want to repeat the code several times and want a decoupled code.
type
TForm1 = class(TForm)
btnTest: TButton;
procedure btnTestClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TBike = class
end;
IBikeWheel = interface
procedure DoBikeWheel;
end;
TBikeWheel = class(TInterfacedObject, IBikeWheel)
public
procedure DoBikeWheel;
end;
IBikeWheelFront = interface
procedure DoBikeWheelFront;
end;
TBikeWheelFront = class(TInterfacedObject, IBikeWheelFront)
public
procedure DoBikeWheelFront;
end;
IBikeWheelBack = interface
end;
TBikeWheelBack = class(TInterfacedObject, IBikeWheelBack)
end;
TBikeWheelFrontXYZ = class(TInterfacedObject, IBikeWheel, IBikeWheelFront)
private
FIBikeWheel: IBikeWheel;
FBikeWheelFront: IBikeWheelFront;
public
constructor Create();
property BikeWheel: IBikeWheel read FIBikeWheel implements IBikeWheel;
property BikeWheelFront: IBikeWheelFront read FBikeWheelFront implements IBikeWheelFront;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TBikeWheel }
procedure TBikeWheel.DoBikeWheel;
begin
ShowMessage('TBikeWheel.DoBikeWheel');
end;
{ TBikeWheelFrontXYZ }
constructor TBikeWheelFrontXYZ.Create;
begin
inherited Create;
Self.FIBikeWheel := TBikeWheel.Create;
Self.FBikeWheelFront := TBikeWheelFront.Create;
end;
{ TBikeWheelFront }
procedure TBikeWheelFront.DoBikeWheelFront;
begin
ShowMessage('TBikeWheelFront.DoBikeWheelFront');
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
bikeWhell: TBikeWheelFrontXYZ;
begin
bikeWhell := nil;
try
try
bikeWhell := TBikeWheelFrontXYZ.Create;
IBikeWheelFront(bikeWhell).DoBikeWheelFront;
IBikeWheel(bikeWhell).DoBikeWheel;
except
on E: Exception do
begin
raise;
end;
end;
finally
if Assigned(bikeWhell) then FreeAndNil(bikeWhell);
end;
end;
Sorry, Delphi does not support Multiple Inheritance.
I would like to suggest the following steps:
Inherit the TBikeWheelFrontXYZ class from either TBikeWheelXYZ or TBikeWheelFront (since in Delphi multiple inheritance is impossible as mentioned in the answers above).
Convert one of the parent classes TBikeWheelXYZ or TBikeWheelFront to class helper for the TBikeWheel class.
Add the class helper unit to the unit, where the TBikeWheelFrontXYZ class is declared.
I have created a couple of interfaces to describe a collection and its items: IetCollection and IetCollectionItem. And of course I have two classes implementing these two interfaces: TetCollection and TetCollectionItem (both inheriting from TInterfacedObject.)
Then I have a series of interfaces where the top level interfaces inherits from IetCollectionItem and the rest from it (lets call them ISomeBasicType and ISomeSpecificType1 and ISomeSpecificType2.)
The class TSomeBasicType inherits from class TetCollectionItem and also implemented ISomeBasicType. The other classes in the hierarchy inherit from TSomeBasicType and implement their respective interfaces (i.e. ISomeSpecificType1 and ISomeSpecificType2.)
When I populate a collection I use a factory method to get a reference to ISomeBasicType. Everything works just fine up to that point.
But when I try to traverse the collection and ask if a collection item supports either ISomeSpecificType1 or ISomeSpecificType2 the answer I get is no.
I have been trying to solve this problem and I have achieved nothing, so any help will be greatly appreciated.
Here is some code:
// This is the basic type
IetCollectionItem = interface
end;
// Implementation of the basic type
TetCollectionItem = class(TInterfacedObject, IetCollectionItem)
end;
ISomeBasicType = interface(IetCollectionItem)
end;
ISomeSpecificType1 = interface(ISomeBasicType)
end;
// Implements ISomeBasicType, should inherit implementation of IetCollectionItem
// from TetCollectionItem
TSomeBasicType = class(TetCollectionItem, ISomeBasicType)
end;
// Implements ISomeSpecificType1, should inherit implementation of ISomeBasicType
// from TSomeBasicType and implementation of IetCollectionItem from
// TetCollectionItem
TSomeSpecificType1 = class(TSomeBasicType, ISomeSpecificType1)
end;
This is the code I user to populate the collection:
var
aBaseType: ISomeBasicType;
aSpecificType: ISomeSpecificType1;
begin
aBaseType:= TheFactory(anID, aType); // Returns a reference to ISomeBasicType
if Supports(aBaseType, ISomeSpecificType1, aSpecificType) then
begin
// Do something to the specific type
aTypeCollection.Add(aSpecificType);
end
else
aTypeCollection.Add(aBaseType);
And here is the code which fails: I loop through the collection and I check to see if any of the items in it support one of the child interfaces.
var
iCount: Integer;
aBaseType: ISomeBasicType;
aSpecificType: ISomeSpecificType1;
begin
for iCount:= 0 to Pred(aTypeCollection.Count) do
begin
aBaseType:= aTypeCollection[iCount];
// This is where Supports fails
if Supports(aBaseType, ISomeSpecificType1, aSpecificType) then
begin
end;
end;
end;
And here is the code for TheFactory:
function TheFactory(const anID: Integer; const aType: TetTypes): ISomeBasicType;
begin
Result:= nil;
case aType of
ptType1 : Result:= TSomeSpecificType1.Create(anID, aType);
ptType2 : Result:= TSomeSpecificType2.Create(anID, aType);
end;
Assert(Assigned(Result), rcUnknonwPhenomenonType);
end; {TheFactory}
Although your code makes me quite dizzy, just from your question title I have a feeling I know where your problem is. Delphi's interface polymorphism unfortunately doesn't behave like Delphi's class polymorphism (I somewhere read that this back in the days had to do with some COM interface compatibility). The point is, that if you are querying a class instance for a specific interface Delphi only finds those interfaces that are directly listed in the class declaration, although another interface in a class declaration might have been inherited from the one you are querying for. See this simple example to understand what I mean.
And sorry, if my answer completly missed your problem.
type
TForm61 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
IBase = interface
['{AE81FB3C-9159-45B0-A863-70FD1365C113}']
end;
IChild = interface(IBase)
['{515771E7-44F6-4819-9B3A-F2A2AFF74543}']
end;
TBase = class(TInterfacedObject, IBase)
end;
TChild = class(TInterfacedObject, IChild)
end;
TChildThatSupportsIbase = class(TChild, IBase)
end;
var
Form61: TForm61;
implementation
{$R *.dfm}
procedure TForm61.Button1Click(Sender: TObject);
var
Child: IChild;
ChildThatSupportsIbase: IChild;
begin
Child := TChild.Create;
ChildThatSupportsIbase:= TChildThatSupportsIbase.Create;
if Supports(Child, IBase) then
ShowMessage('TChild supports IBase')
else
ShowMessage('TChild doesn''t supports IBase');
if Supports(ChildThatSupportsIbase, IBase) then
ShowMessage('TChildThatSupportsIbase supports IBase')
else
ShowMessage('TChildThatSupportsIbase doesn''t supports IBase');
end;
Sample code edited to use your class hierarchy. Both Supports calls return True. I only added GUID's to your interfaces.
If my crystal ball is in working order, you forgot to give your interfaces GUID's.
Here's a proof that what I think you're asking works. If this is not what you're asking, take the hint and replace the code block with a short but complete console application that clearly displays the problem:
program Project29;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
// This is the basic type
IetCollectionItem = interface
end;
// Implementation of the basic type
TetCollectionItem = class(TInterfacedObject, IetCollectionItem)
end;
ISomeBasicType = interface(IetCollectionItem)
['{F082CD83-5030-42EE-A1A8-FF91769F986F}']
end;
ISomeSpecificType1 = interface(ISomeBasicType)
['{8789FD5A-FC94-4F19-B28B-8ABA67D66DAE}']
end;
// Implements ISomeBasicType, should inherit implementation of IetCollectionItem
// from TetCollectionItem
TSomeBasicType = class(TetCollectionItem, ISomeBasicType)
end;
// Implements ISomeSpecificType1, should inherit implementation of ISomeBasicType
// from TSomeBasicType and implementation of IetCollectionItem from
// TetCollectionItem
TSomeSpecificType1 = class(TSomeBasicType, ISomeSpecificType1)
end;
var iBase: IetCollectionItem;
begin
iBase := TSomeSpecificType1.Create;
if Supports(iBase, iSomeBasicType) then
WriteLn('iBase supports iSomeBasicType')
else
WriteLn('iBase does not support iSomeBasicType');
if Supports(iBase, ISomeSpecificType1) then
WriteLn('iBase supports ISomeSpecificType1')
else
WriteLn('iBase does not support ISomeSpecificType1');
WriteLn('Press ENTER'); Readln;
end.
First you place something which clearly does NOT support ISomeSpecificType1 in the list:
if Supports(aBaseType, ISomeSpecificType1, aSpecificType) then
begin
// Do something to the specific type
aTypeCollection.Add(aSpecificType);
end
else
aTypeCollection.Add(aBaseType); //<------- this
Then you wonder why it does not support ISomeSpecificType1.
What's the problem exactly? Why do you think all or even ANY of the items from the collection should support ISomeSpecificType1?
It could have been that every single item you have added did not support it.
If I have an existing IInterface descendant implemented by a third party, and I want to add helper routines, does Delphi provide any easy way to do so without redirecting every interface method manually? That is, given an interface like so:
IFoo = interface
procedure Foo1;
procedure Foo2;
...
procedure FooN;
end;
Is anything similar to the following supported?
IFooHelper = interface helper for IFoo
procedure Bar;
end;
or
IFooBar = interface(IFoo)
procedure Bar;
end;
TFooBar = class(TInterfacedObject, IFoo, IFooBar)
private
FFoo: IFoo;
public
procedure Bar;
property Foo: IFoo read FFoo implements IFoo;
end;
I'm specifically wondering about ways to that allow me to always call Foo1, Foo2, and Bar with a single variable reference (IFoo, IFooBar, or TFooBar), without switching between them, and without adding all of IFoo's methods to TFooBar.
Change your class to read:
TFooBar = Class(TInterfacedObject, IFoo, IFooBar)
private
FFoo: IFoo;
public
procedure Bar;
property Foo: IFoo read FFoo implements IFoo ;
end;
You will also need a constructor or some method to create the instance of IFoo and place it in FFoo.
You cannot access the methods of IFoo through a reference to TFooBar because TFooBar doesn't implement IFoo - it delegates IFoo. But you shouldn't be using a TFooBar reference to access an interfaced object anyway, that's the whole point of using interfaces in the first place!
NOTE: To deliberately prevent this I adopt a convention of implementing interface methods on my classes as "protected", specifically to deny access to those implementation details except as surfaced via the interface itself.
Where-ever you are currently obtaining a reference to TFooBar, change this to instead obtain the IFooBar interface implemented by TFooBar and you will be sorted.
I'm not sure I understood all of your concerns, but here's my suggestion anyway :
IFooBar = interface(IFoo)
procedure Bar;
end;
TFooDelegate = class(TInterfacedObject, IFoo )
private
FFoo: IFoo;
public
property Foo: IFoo read FFoo implements IFoo;
end;
TFooBar = class( TFooDelegate, IFooBar )
procedure Bar;
end;
see
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces:_Delphi_and_C%2B%2B
on how to use TAggregatedObject together with "implements" syntax at a property
quoting from there:
unit Unit1;
interface
type
// Interface that exposes an 'Add' method
IAdder = interface
['{D0C74612-9E4D-459A-9304-FACE27E3577D}']
function Add(I, J: Integer): Integer;
end;
// Aggregatee that implements IAdder
TAdder = class(TAggregatedObject, IAdder)
function Add(I, J: Integer): Integer;
end;
// Aggregator - implements IAdder via TAdder
TPasClass = class(TInterfacedObject, IAdder)
FAdder: TAdder;
function GetAdder: TAdder;
public
destructor Destroy; override;
property Adder: TAdder read GetAdder write FAdder implements IAdder;
end;
function TestAdd(I, J: Integer): Integer;
implementation
{ TAdder }
function TAdder.Add(I, J: Integer): Integer;
begin
Result := I+J;
end;
{ TPasClass }
destructor TPasClass.Destroy;
begin
FAdder.Free;
inherited;
end;
function TPasClass.GetAdder: TAdder;
begin
if FAdder = nil then
FAdder := TAdder.Create(Self as IInterface);
Result := FAdder;
end;
// Adds using TClass' IAdder
function TestAdd(I, J: Integer): Integer;
var
punk: IInterface;
begin
punk := TPasClass.Create as IInterface;
Result := (punk as IAdder).Add(I, J);
end;
end.
however it would be nice if one could do:
TSomeClassIFoo123Helper = class helper(IFoo1, IFoo2, IFoo3) for TSomeClass
procedure Bar1;
procedure Bar2;
procedure Bar3;
end;
injecting IFoo1, IFoo2, IFoo3 interface implementations into TSomeClass. The helpers support inheritance, but can only specify another helper class at the list, not one ore more interfaces there