I have the following code
IProxy<T> = interface
['{1E3A98C5-78BA-4D65-A4BA-B6992B8B4783}']
function Setup : ISetup<T>;
function Proxy : T;
function CastAs<I : IInterface> : IInterface;
end;
Is there a way to work around the compiler error that is received when compiling?
"[DCC Error] Delphi.Mocks.pas(123): E2535 Interface methods must not have parameterized methods"
Basically I would like to have this interface be passed around and be able to cast off it, by passing in the type to cast to and having that type returned. I can achieve this with a class, however would prefer passing around an interface.
Additional Info:
Say I have the following class
TInterfaceProxy<T> = class(TBaseProxy<T>)
private type
TProxyVirtualInterface = class(TVirtualInterface)
private
FProxy : TInterfaceProxy<T>;
protected
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; override; stdcall;
constructor Create(AProxy : TInterfaceProxy<T>; AInterface: Pointer; InvokeEvent: TVirtualInterfaceInvokeEvent);
end;
private
FVirtualInterfaces : TDictionary<TGUID, TProxyVirtualInterface>;
protected
function InternalQueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HRESULT; override;
function Proxy : T;override;
function CastAs<I: IInterface> : I;
public
constructor Create;override;
destructor Destroy;override;
end;
CastAs works well here as the newly requested cast can be created a new virtual interface. Now if I want to pass this class around its fine. However if I require it as an interface i.e. TInterfaceProxy<T> = class(TBaseProxy<T>, IProxy<T>)
it doesn't work understand that. Don't agree with it, but understand.
Therefore how do I get around this restriction so that I can call a CastAs function, pass in a type (any interface type to start with), and be able to create a virtual interface from it?
Generics overview Use generic types to maximize code reuse, type safety, and performance. The most common use of generics is to create collection classes. The . NET class library contains several generic collection classes in the System.
In Delphi, "interface" has two distinct meanings. In OOP jargon, you can think of an interface as a class with no implementation. In Delphi unit definition interface section is used to declare any public sections of code that appear in a unit. This article will explain interfaces from an OOP perspective.
Something like the following compiles for me with Delphi 11.0 (yet to see if it crashes):
TStoryItemList = TList<IStoryItem>;
TObjectListEx<T: class> = class
class function GetAllOfInterface<AInterface: IInterface>(const list: TList<T>): TList<AInterface>;
end;
//...
class function TObjectListEx<T>.GetAllOfInterface<AInterface>(const list: TList<T>): TList<AInterface>;
var
itemAsAInterface: AInterface;
begin
var guid := TRttiInterfaceType(TRttiContext.Create.GetType(TypeInfo(AInterface))).GUID;
var listOfAInterface := TList<AInterface>.Create;
for var item in list do
if Supports(item, guid, itemAsAInterface) then
listOfAInterface.Add(itemAsAInterface);
result := listOfAInterface;
end;
usage
function TStoryItem.GetStoryItems: TStoryItemList;
begin
result := TObjectListEx<TControl>.GetAllOfInterface<IStoryItem>(Controls);
end;
Interfaces do not support generic parameterized methods, as the compiler says.
There is no workaround because it's a fundamental limitation. Parameterized methods in classes are implemented by adding one method per instantiation to the class. That works for classes since they are concrete, but is not viable for interfaces. That's because interfaces are a table of functions and the size of that table cannot vary depending on which generic method instantiations happen to be present elsewhere in the code. For similar reasons, generic methods cannot be virtual or dynamic.
The code in your question is a little mis-leading also. You wrote:
function CastAs<I : IInterface> : IInterface;
but I'm sure you meant:
function CastAs<I : IInterface> : I;
In any case, it's not possible. One option is to use a class instead. I agree that this is a bind.
If you want to do it in an interface, the best you can do is:
function CastAs(const IID: TGUID): IInterface;
But you'd have to call it like this:
MyIntf := ProxyIntf.CastAs(IMyIntf) as IMyIntf;
which feels somewhat foul.
Choose your poison!
As the error message states, a method in an interface cannot have Generic parameters. The compiler simply does not support it, and this is documented as such:
http://docwiki.embarcadero.com/RADStudio/XE4/en/Overview_of_Generics
Parameterized method in interface
A parameterized method (method declared with type parameters) cannot be declared in an interface.
In other words, your CastAs
method is illegal because it is declared in an interface type. On the other hand, you don't need such a method in the first place. You can use SysUtils.Supports()
instead for casting one interface to another.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With