问题
In the sample code below, the Run<T>()
displays the values of GUID IFoo
and IFoo<T>
interfaces:
type
IBar = interface
['{992E6597-42F1-40F8-B678-C4A86864B030}']
end;
IFoo = interface
['{0C589AF8-5727-4EAA-BB41-6D51D70B9D35}']
end;
IFoo<T> = interface(IFoo)
['{8FF54F6B-0896-4EA3-85F8-66BA70F9D2DA}']
end;
TTest = class
public
class procedure Run<T: IFoo>;
end;
class procedure TTest.Run<T>;
var
LContext: TRttiContext;
IFoo_T_TypeInfo: PTypeInfo;
IFooTypeInfo: PTypeInfo;
begin
IFoo_T_TypeInfo := TypeInfo(T);
IFooTypeInfo := LContext.GetType(TypeInfo(T)).BaseType.Handle;
WriteLn('IFoo<T> GUID: ', GetTypeData(IFoo_T_TypeInfo).GUID.ToString);
WriteLn('IFoo GUID: ', GetTypeData(IFooTypeInfo).GUID.ToString);
WriteLn('IBar GUID: ', '?');
end;
begin
TTest.Run<IFoo<IBar>>;
ReadLn;
end.
Is there a way to get TGUID
or PTypeInfo
from a generic constraint type, IBar
interface in this case?
P.S.: I wouldn't like change the signature of Run<T>()
to Run<T, U>()
for just get the IBar
GUID from U.
回答1:
Getting typeinfo/RTTI from generic type parameters is a bit tricky but not completely impossible.
Here is some example code how to do that (I am using the RTTI extensions from the Spring.Reflections unit).
uses
Rtti,
SysUtils,
Spring.Reflection;
type
TTest = class
public
class procedure Run<T: IFoo>;
end;
class procedure TTest.Run<T>;
var
LType, LType2: TRttiType;
begin
LType := TType.GetType(TypeInfo(T));
if LType.IsInterface then
begin
if LType.AsInterface.HasGuid then
Writeln(LType.Name, ' GUID: ', LType.AsInterface.GUID.ToString);
LType2 := LType.BaseType;
while Assigned(LType2) and (LType2.Handle <> TypeInfo(IInterface)) do
begin
if LType2.AsInterface.HasGuid then
Writeln(LType2.Name, ' GUID: ', LType2.AsInterface.GUID.ToString);
LType2 := LType2.BaseType;
end;
if LType.IsGenericType then
begin
for LType2 in LType.GetGenericArguments do
if Assigned(LType2) and LType2.IsInterface then
Writeln(LType2.Name, ' GUID: ', LType2.AsInterface.GUID.ToString);
end;
end
end;
var
bar: IBar;
begin
bar := TBar.Create; // cause RTTI for IBar to be generated to look it up later
TTest.Run<IFoo<IBar>>;
ReadLn;
end.
The check if the type is generic is done via string parsing of the type name. If it contains angle brackets it is a generic type. It then extracts the type names which are always full qualified type names which makes it possible to look them up.
However there is one gotcha to keep in mind. You can only look those up when the type info for that type was generated in some other context than just the generic type parameter. That is why in that sample I made a simple TBar class that implements IBar and created some instance to prevent the linker to strip that class (and the necessary RTTI). In real code this is less of an issue because you typically have some implementations of that interface. Also for this example to work you need to put the interface into their own unit because lookup by full qualified name does not work for types in the dpr.
来源:https://stackoverflow.com/questions/51368925/is-there-a-way-to-get-guid-from-a-generic-constraint-type