Delphi 使用Rtti和泛型列表调用GetEnumerator发生访问冲突

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

这个过程自动调用TObject(AObj)的枚举器:对于TForm很好,但是对于泛型列表(例如TList)失败! 此处的访问冲突:“值:=Current.GetValue(EnumeratorObj);”为什么? 错误发生前如何检查

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?这是什么?我不知道抱歉。用网络搜索来发现它的意思。