在DelphiXE3中,如何使用TypeInfo或RTTI将TVirtualInterface对象强制转换到其接口?
我正在尝试使用TVirtualInterface。我主要是在和上尝试遵循这些例子 然而,我试图做的与标准示例略有不同 我已经尽可能地简化了下面的示例代码,以说明我要做的事情。我省略了明显的验证和错误处理代码在DelphiXE3中,如何使用TypeInfo或RTTI将TVirtualInterface对象强制转换到其接口?,delphi,interface,casting,rtti,delphi-xe3,Delphi,Interface,Casting,Rtti,Delphi Xe3,我正在尝试使用TVirtualInterface。我主要是在和上尝试遵循这些例子 然而,我试图做的与标准示例略有不同 我已经尽可能地简化了下面的示例代码,以说明我要做的事情。我省略了明显的验证和错误处理代码 program VirtualInterfaceTest; {$APPTYPE CONSOLE} {$R *.res} uses System.Generics.Collections, System.Rtti, System.SysUtils, System.TypI
program VirtualInterfaceTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.Generics.Collections,
System.Rtti,
System.SysUtils,
System.TypInfo;
type
ITestData = interface(IInvokable)
['{6042BB6F-F30C-4C07-8D3B-C123CF1FF60F}']
function GetComment: string;
procedure SetComment(const Value: string);
property Comment: string read GetComment write SetComment;
end;
IMoreData = interface(IInvokable)
['{1D2262CE-09F4-45EC-ACD8-3EEE6B2F1548}']
function GetSuccess: Boolean;
procedure SetSuccess(const Value: Boolean);
property Success: Boolean read GetSuccess write SetSuccess;
end;
TDataHolder = class
private
FTestData: ITestData;
FMoreData: IMoreData;
public
property TestData: ITestData read FTestData write FTestData;
property MoreData: IMoreData read FMoreData write FMoreData;
end;
TVirtualData = class(TVirtualInterface)
private
FData: TDictionary<string, TValue>;
procedure DoInvoke(Method: TRttiMethod;
const Args: TArray<TValue>;
out Result: TValue);
public
constructor Create(PIID: PTypeInfo);
destructor Destroy; override;
end;
constructor TVirtualData.Create(PIID: PTypeInfo);
begin
inherited Create(PIID, DoInvoke);
FData := TDictionary<string, TValue>.Create;
end;
destructor TVirtualData.Destroy;
begin
FData.Free;
inherited Destroy;
end;
procedure TVirtualData.DoInvoke(Method: TRttiMethod;
const Args: TArray<TValue>;
out Result: TValue);
var
key: string;
begin
if (Pos('Get', Method.Name) = 1) then
begin
key := Copy(Method.Name, 4, MaxInt);
FData.TryGetValue(key, Result);
end;
if (Pos('Set', Method.Name) = 1) then
begin
key := Copy(Method.Name, 4, MaxInt);
FData.AddOrSetValue(key, Args[1]);
end;
end;
procedure InstantiateData(obj: TObject);
var
rttiContext: TRttiContext;
rttiType: TRttiType;
rttiProperty: TRttiProperty;
propertyType: PTypeInfo;
data: IInterface;
value: TValue;
begin
rttiContext := TRttiContext.Create;
try
rttiType := rttiContext.GetType(obj.ClassType);
for rttiProperty in rttiType.GetProperties do
begin
propertyType := rttiProperty.PropertyType.Handle;
data := TVirtualData.Create(propertyType) as IInterface;
value := TValue.From<IInterface>(data);
// TValueData(value).FTypeInfo := propertyType;
rttiProperty.SetValue(obj, value); // <<==== EInvalidCast
end;
finally
rttiContext.Free;
end;
end;
procedure Test_UsingDirectInstantiation;
var
dataHolder: TDataHolder;
begin
dataHolder := TDataHolder.Create;
try
dataHolder.TestData := TVirtualData.Create(TypeInfo(ITestData)) as ITestData;
dataHolder.MoreData := TVirtualData.Create(TypeInfo(IMoreData)) as IMoreData;
dataHolder.TestData.Comment := 'Hello World!';
dataHolder.MoreData.Success := True;
Writeln('Comment: ', dataHolder.TestData.Comment);
Writeln('Success: ', dataHolder.MoreData.Success);
finally
dataHolder.Free;
end;
end;
procedure Test_UsingIndirectInstantiation;
var
dataHolder: TDataHolder;
begin
dataHolder := TDataHolder.Create;
try
InstantiateData(dataHolder); // <<====
dataHolder.TestData.Comment := 'Hello World!';
dataHolder.MoreData.Success := False;
Writeln('Comment: ', dataHolder.TestData.Comment);
Writeln('Success: ', dataHolder.MoreData.Success);
finally
dataHolder.Free;
end;
end;
begin
try
Test_UsingDirectInstantiation;
Test_UsingIndirectInstantiation;
except on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
程序虚拟接口测试;
{$APPTYPE控制台}
{$R*.res}
使用
System.Generics.Collections,
系统,Rtti,,
System.SysUtils,
System.TypInfo;
类型
ITestData=接口(IInvokable)
[{6042BB6F-F30C-4C07-8D3B-C123CF1FF60F}]
函数GetComment:string;
过程SetComment(常量值:字符串);
属性注释:字符串读取GetComment写入SetComment;
结束;
IMoreData=接口(IInvokable)
['{1D22262CE-09F4-45EC-ACD8-3EEE6B2F1548}']
函数GetSuccess:Boolean;
过程SetSuccess(常量值:布尔值);
属性Success:Boolean read GetSuccess write SetSuccess;
结束;
TDataHolder=class
私有的
FTestData:ITestData;
FMoreData:伊莫雷达;
公众的
属性TestData:ITestData读取FTestData写入FTestData;
属性MoreData:IMoreData读取FMoreData写入FMoreData;
结束;
TVirtualData=类(TVirtualInterface)
私有的
FData:t词典;
程序DoInvoke(方法:TRTTI法;
常数Args:TArray;
输出结果:TValue);
公众的
构造函数创建(PIID:PTypeInfo);
毁灭者毁灭;推翻
结束;
构造函数TVirtualData.Create(PIID:PTypeInfo);
开始
继承的创建(PIID、DoInvoke);
FData:=TDictionary.Create;
结束;
析构函数TVirtualData.Destroy;
开始
自由数据;
继承性破坏;
结束;
程序TVirtualData.DoInvoke(方法:Trti方法;
常数Args:TArray;
输出结果:TValue);
变量
键:字符串;
开始
如果(Pos('Get',Method.Name)=1),则
开始
key:=复制(Method.Name,4,MaxInt);
FData.TryGetValue(键、结果);
结束;
如果(Pos('Set',Method.Name)=1),则
开始
key:=复制(Method.Name,4,MaxInt);
FData.AddOrSetValue(键,参数[1]);
结束;
结束;
程序实例化数据(对象:TObject);
变量
rttiContext:TRttiContext;
rttType:trtType;
rttiProperty:TRttiProperty;
propertyType:PTypeInfo;
数据:界面;
价值:TValue;
开始
rttiContext:=TRttiContext.Create;
尝试
rttiType:=rttiContext.GetType(obj.ClassType);
对于rttiType.GetProperties中的rttiProperty,请执行以下操作:
开始
propertyType:=rttiProperty.propertyType.Handle;
数据:=TVirtualData.Create(propertyType)作为界面;
值:=TValue.From(数据);
//TValueData(value).FTypeInfo:=propertyType;
rttiProperty.SetValue(对象,值);// 首先,必须将实例强制转换为正确的接口,而不是I接口。您仍然可以将其存储在IInterface变量中,但它确实包含对正确接口类型的引用
然后,您必须将其放入具有正确类型的TValue,而不是IInterface(RTTI对类型非常严格)
您添加的注释行只是为了解决第二个问题,但由于它确实包含IInterface引用(而不是ITestData或TMoreData引用),因此它在AV上产生
procedure InstantiateData(obj: TObject);
var
rttiContext: TRttiContext;
rttiType: TRttiType;
rttiProperty: TRttiProperty;
propertyType: PTypeInfo;
data: IInterface;
value: TValue;
begin
rttiType := rttiContext.GetType(obj.ClassType);
for rttiProperty in rttiType.GetProperties do
begin
propertyType := rttiProperty.PropertyType.Handle;
Supports(TVirtualData.Create(propertyType), TRttiInterfaceType(rttiProperty.PropertyType).GUID, data);
TValue.Make(@data, rttiProperty.PropertyType.Handle, value);
rttiProperty.SetValue(obj, value);
end;
end;
只是一个旁注——您不必创建TRttiContext的实例——它将在第一次使用时自动实例化。而且您也不需要释放它!谢谢你。很高兴知道。这绝对解决了我的问题。我几天前就该问了。