Delphi 使用Rtti和泛型列表调用GetEnumerator发生访问冲突
这个过程自动调用TObject(AObj)的枚举器:对于TForm很好,但是对于泛型列表(例如TList)失败! 此处的访问冲突:“值:=Current.GetValue(EnumeratorObj);”为什么? 错误发生前如何检查Delphi 使用Rtti和泛型列表调用GetEnumerator发生访问冲突,delphi,generics,rtti,delphi-xe4,enumerator,Delphi,Generics,Rtti,Delphi Xe4,Enumerator,这个过程自动调用TObject(AObj)的枚举器:对于TForm很好,但是对于泛型列表(例如TList)失败! 此处的访问冲突:“值:=Current.GetValue(EnumeratorObj);”为什么? 错误发生前如何检查 procedure (const ARType:TRttiType; AObj:TObject; ANode:PNode) var Current: TRttiProperty; EnumType: TRttiType; MoveNext: TRttiM
procedure (const ARType:TRttiType; AObj:TObject; ANode:PNode)
var
Current: TRttiProperty;
EnumType: TRttiType;
MoveNext: TRttiMethod;
GetEnumerator:TRttiMethod;
EnumeratorObj:TObject;
LObj:TObject;
Value: TValue;
begin// Call Enumerator
GetEnumerator := ARType.GetMethod('GetEnumerator');
if (not Assigned(GetEnumerator)) or (GetEnumerator.MethodKind <> mkFunction)
or (GetEnumerator.ReturnType.Handle.Kind <> tkClass) then exit;
//
EnumeratorObj := GetEnumerator.Invoke(AObj, []).AsObject;
if not Assigned(EnumeratorObj) then exit;
EnumType := _Cxt.GetType(EnumeratorObj.ClassInfo);
// Find the Current property
Current := EnumType.GetProperty('Current');
if (not Assigned(Current)) or
not (Current.PropertyType.TypeKind in [tkString, tkUString, tkClass]) then exit;
// Find the MoveNext property
MoveNext := EnumType.GetMethod('MoveNext');
if (not Assigned(MoveNext)) or (Length(MoveNext.GetParameters) > 0) or
(MoveNext.MethodKind <> mkFunction) or
(MoveNext.ReturnType.Handle <> TypeInfo(Boolean)) then exit;
// while MoveNext do
while MoveNext.Invoke(EnumeratorObj, []).AsBoolean do
begin
// Value := Current
Value := Current.GetValue(EnumeratorObj); //!!!error here!!!
if Value.Kind = tkClass then
begin
LObj := Value.AsObject;
if Assigned(LObj) then
ANode.Action(_Cxt.GetType(LObj.ClassInfo), LObj, ANode.Next);//Recursif!!!
end
else break;
end;
程序(常数类型:TrtType;AObj:ToObject;阳极:PNode)
变量
当前:TRTTI属性;
枚举类型:TRttiType;
下一步:TRTTI方法;
GetEnumerator:trtti方法;
EnumeratorBj:TObject;
LObj:TObject;
价值:TValue;
开始//调用枚举器
GetEnumerator:=ARType.GetMethod('GetEnumerator');
if(未分配(GetEnumerator))或(GetEnumerator.MethodKind mkFunction)
或者(GetEnumerator.ReturnType.Handle.Kind tkClass),然后退出;
//
EnumeratorObject:=GetEnumerator.Invoke(AObj,[]).AsObject;
如果未分配(枚举器BJ),则退出;
EnumType:=\u Cxt.GetType(EnumeratorObj.ClassInfo);
//查找当前属性
当前:=EnumType.GetProperty('Current');
如果(未分配(当前))或
not(当前[tkString,tkString,tkClass]中的Current.PropertyType.TypeKind)然后退出;
//查找下一个属性
MoveNext:=EnumType.GetMethod('MoveNext');
如果(未分配(MoveNext))或(长度(MoveNext.GetParameters)>0)或
(MoveNext.MethodKind mkFunction)或
(MoveNext.ReturnType.Handle TypeInfo(布尔值))然后退出;
//下一步做什么
而MoveNext.Invoke(EnumeratorObj,[]).AsBoolean do
开始
//值:=当前值
值:=Current.GetValue(EnumeratorObj);/!!!这里有错误!!!
如果Value.Kind=tkClass,则
开始
LObj:=Value.AsObject;
如果已分配(LObj),则
Action(Cxt.GetType(LObj.ClassInfo),LObj,ANode.Next)//如果!!!
结束
否则就断了;
结束;
例如:
uses rtti, typinfo;
procedure TForm1.Button4Click(Sender: TObject);
var
_Cxt :TRttiContext;
List :TList<TObject>;
procedure Exec(const ARType:TRttiType; AObj:TObject; ANode:TObject);
var
Current: TRttiProperty;
EnumType: TRttiType;
MoveNext: TRttiMethod;
GetEnumerator:TRttiMethod;
EnumeratorObj:TObject;
LObj:TObject;
Value: TValue;
begin// Call Enumerator
GetEnumerator := ARType.GetMethod('GetEnumerator');
if (not Assigned(GetEnumerator)) or (GetEnumerator.MethodKind <> mkFunction)
or (GetEnumerator.ReturnType.Handle.Kind <> tkClass) then exit;
//
EnumeratorObj := GetEnumerator.Invoke(AObj, []).AsObject;
if not Assigned(EnumeratorObj) then exit;
EnumType := _Cxt.GetType(EnumeratorObj.ClassInfo);
// Find the Current property
Current := EnumType.GetProperty('Current');
if (not Assigned(Current)) or
not (Current.PropertyType.TypeKind in [tkString, tkUString, tkClass]) then exit;
// Find the MoveNext property
MoveNext := EnumType.GetMethod('MoveNext');
if (not Assigned(MoveNext)) or (Length(MoveNext.GetParameters) > 0) or
(MoveNext.MethodKind <> mkFunction) or
(MoveNext.ReturnType.Handle <> TypeInfo(Boolean)) then exit;
// while MoveNext do
while MoveNext.Invoke(EnumeratorObj, []).AsBoolean do
begin
// Value := Current
Value := Current.GetValue(EnumeratorObj); //!!boooom!! Access violation in XE4
if Value.Kind = tkClass then
begin
LObj := Value.AsObject;
// if Assigned(LObj) then
// ANode.Action(_Cxt.GetType(LObj.ClassInfo), LObj, ANode.Next);//Recursif!!!
end
else break;
end;
end;
begin
_Cxt := TRttiContext.Create;
List := TList<TObject>.Create;
List.Add(TObject(666));
try
Exec(_Cxt.GetType(List.ClassInfo), List, nil); // it's NOT OK
Exec(_Cxt.GetType(Self.ClassInfo), Self, nil); // it's OK
finally
List.Free;
_Cxt.Free;
end;
end;
使用rtti、typinfo;
程序TForm1.按钮4点击(发送方:TObject);
变量
_Cxt:trtti上下文;
名单:TList ;;
程序执行(常数类型:TrtType;AObj:TObject;阳极:TObject);
变量
当前:TRTTI属性;
枚举类型:TRttiType;
下一步:TRTTI方法;
GetEnumerator:trtti方法;
EnumeratorBj:TObject;
LObj:TObject;
价值:TValue;
开始//调用枚举器
GetEnumerator:=ARType.GetMethod('GetEnumerator');
if(未分配(GetEnumerator))或(GetEnumerator.MethodKind mkFunction)
或者(GetEnumerator.ReturnType.Handle.Kind tkClass),然后退出;
//
EnumeratorObject:=GetEnumerator.Invoke(AObj,[]).AsObject;
如果未分配(枚举器BJ),则退出;
EnumType:=\u Cxt.GetType(EnumeratorObj.ClassInfo);
//查找当前属性
当前:=EnumType.GetProperty('Current');
如果(未分配(当前))或
not(当前[tkString,tkString,tkClass]中的Current.PropertyType.TypeKind)然后退出;
//查找下一个属性
MoveNext:=EnumType.GetMethod('MoveNext');
如果(未分配(MoveNext))或(长度(MoveNext.GetParameters)>0)或
(MoveNext.MethodKind mkFunction)或
(MoveNext.ReturnType.Handle TypeInfo(布尔值))然后退出;
//下一步做什么
而MoveNext.Invoke(EnumeratorObj,[]).AsBoolean do
开始
//值:=当前值
值:=当前的.GetValue(枚举器BJ);/!!嘘!!XE4中的访问冲突
如果Value.Kind=tkClass,则
开始
LObj:=Value.AsObject;
//如果已分配(LObj),则
//Action(Cxt.GetType(LObj.ClassInfo),LObj,ANode.Next)//如果!!!
结束
否则就断了;
结束;
结束;
开始
_Cxt:=TRttiContext.Create;
列表:=TList.Create;
增加(TObject(666));
尝试
Exec(_Cxt.GetType(List.ClassInfo),List,nil);//不好
Exec(_Cxt.GetType(Self.ClassInfo),Self,nil);//没关系
最后
列表。免费;
_Cxt.免费;
结束;
结束;
问题在于TObject(666)
不是一个真实的对象。只要使用实际的对象实例,或者nil
,代码就可以工作
访问冲突的原因是GetValue
方法最终调用TValue.Make(指针、PTypeInfo、out TValue)
重载,其中包括以下代码:
// make a better-educated guess about type-info when we can
case ATypeInfo^.Kind of
tkClass:
if Result.FData.FAsObject <> nil then
Result.FData.FTypeInfo :=
GetClassInfo(TObject(Result.FData.FAsObject).ClassType);
end;
//尽可能对类型信息进行更好的猜测
案例ATypeInfo^。有点
tkClass:
如果Result.FData.FAsObject为零,则
Result.FData.FTypeInfo:=
GetClassInfo(TObject(Result.FData.FAsObject).ClassType);
结束;
代码要求对象是真实的对象实例。该代码正在尝试获取实例的类信息。它通过ATypeInfo
接收属性的类型信息,然后尝试使用特定实例的实际类型信息。由于TObject(666)
不是真实的对象实例,因此代码结果导致运行时错误。对此表示抱歉。此过程位于一个匿名方法中:Action(procedure())。您的代码在这里工作。你需要做一个SSCCE,SSCCE?这是什么?我不知道抱歉。用网络搜索来发现它的意思。