Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度?_Delphi_Dynamic_Arrays_Rtti - Fatal编程技术网

Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度?

Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度?,delphi,dynamic,arrays,rtti,Delphi,Dynamic,Arrays,Rtti,我想设置动态数组的长度,如中所建议的。我有两个类TMyClass和相关的TChildClass,定义为 TChildClass = class private FField1: string; FField2: string; end; TMyClass = class private FField1: TChildClass; FField2: Array of TChildClass; end; 阵列扩充的实现如下所示: var RContext: T

我想设置动态数组的长度,如中所建议的。我有两个类TMyClass和相关的TChildClass,定义为

TChildClass = class
private
  FField1:  string;
  FField2:  string;
end;

TMyClass = class
private
  FField1:  TChildClass;
  FField2:  Array of TChildClass;
end;
阵列扩充的实现如下所示:

var
  RContext:     TRttiContext;
  RType:        TRttiType;
  Val:          TValue;      // Contains the TMyClass instance
  RField:       TRttiField;  // A field in the TMyClass instance
  RElementType: TRttiType;   // The kind of elements in the dyn array
  DynArr:       TRttiDynamicArrayType;
  Value:        TValue;  // Holding an instance as referenced by an array element
  ArrPointer:   Pointer;
  ArrValue:     TValue;
  ArrLength:    LongInt;
  i:            integer;
begin
  RContext := TRTTIContext.Create;
  try
    RType := RContext.GetType(TMyClass.ClassInfo);
    Val := RType.GetMethod('Create').Invoke(RType.AsInstance.MetaclassType, []);
    RField := RType.GetField('FField2');
    if (RField.FieldType is TRttiDynamicArrayType) then begin 
      DynArr := (RField.FieldType as TRttiDynamicArrayType);
      RElementType := DynArr.ElementType;
      // Set the new length of the array
      ArrValue := RField.GetValue(Val.AsObject);
      ArrLength := 3;   // Three seems like a nice number
      ArrPointer := ArrValue.GetReferenceToRawData;
      DynArraySetLength(ArrPointer, ArrValue.TypeInfo, 1, @ArrLength);
      { TODO : Fix 'Index out of bounds' }
      WriteLn(ArrValue.IsArray, ' ', ArrValue.GetArrayLength);
      if RElementType.IsInstance then begin
        for i := 0 to ArrLength - 1 do begin
          Value := RElementType.GetMethod('Create').Invoke(RElementType.AsInstance.MetaclassType, []);
          ArrValue.SetArrayElement(i, Value);
          // This is just a test, so let's clean up immediatly
          Value.Free;
        end;
      end;
    end;
    ReadLn;
    Val.AsObject.Free;
  finally
    RContext.Free;
  end;
end.

作为D2010 RTTI的新手,我怀疑错误可能取决于从类实例获取ArrValue,但随后的
WriteLn
打印“TRUE”,因此我排除了这种可能性。然而,令人失望的是,相同的
WriteLn
报告ArrValue的大小为0,这由“索引超出边界”确认——当我尝试设置数组中的任何元素时(通过
ArrValue.SetArrayElement(I,Value);
)得到的异常。有人知道我做错了什么吗?(或者有更好的方法吗?)

我认为应该将数组定义为单独的类型:

TMyArray = array of TMyClass;
用这个

从一个旧的基于RTTI的XML序列化程序中,我知道您应该使用的通用方法(D7..2009测试):

过程TXMLImpl.ReadArray(常量名称:字符串;类型信息:TArrayInformation;数据:指针;IO:TParameterInputOutput);
变量
P:PChar;
五十、 D:整数;
BT:t类型信息;
开始
FArrayType:='';
FArraySize:=-1;
ComplexTypePrefix(名称“”);
尝试
//获取元素类型信息。
BT:=TypeInfo.BaseType;
如果未分配(BT),则RaiseSerializationReadError;//不是受支持的数据类型!
//键入检查数组说明符。
如果是(FArrayType“”)和(FArrayType GetTypeName(BT)),则RaiseSerializationReadError;
//我们有固定大小的数组还是动态大小的数组?
L:=法拉利尺寸;
如果L>=0,则开始
//设置数组
DynArraySetLength(点(数据)^,TypeInfo.TypeInformation,1,@L);
//恢复元素
D:=TypeInfo.ElementSize;
P:=P点(数据)^;
当L>0时,开始
ReadElement(''{ArrayItemName},BT,P,IO);//我们允许使用任何数组项名称。
公司(P,D),;
12月(L);
结束;
结束,否则开始
支持的理由;
结束;
最后
复合型后缀;
结束;
结束;

希望这能有所帮助。

使用动态数组有点棘手。它们是引用计数的,DynArraySetLength中的以下注释应该可以说明问题:

//如果堆对象没有共享(ref count=1),只需调整其大小即可。否则,我们复制一份

您的对象包含一个对它的引用,TValue也是如此。另外,GetReferenceToRawData为您提供了一个指向数组的指针。您需要说
PPointer(GetReferenceToRawData)^
以获得要传递给DynArraySetLength的实际数组

一旦你有了它,你可以调整它的大小,但你留下了一个副本。然后必须将其设置回原始数组

TValue.Make(@ArrPointer, dynArr.Handle, ArrValue);
RField.SetValue(val.AsObject, arrValue);

总之,使用列表而不是数组可能要简单得多。D2010提供了泛型。集合,这意味着您可以创建
TList
TObjectList
,并在不丢失类型安全性的情况下获得列表类的所有好处。

谢谢Mason,我想我应该在询问之前仔细研究一下。。。无论如何;最近我觉得自己很懒,而TList和/或TObjectList更方便,所以我会让它转一转。:)+1我只需要相同的,它工作正常,但每次添加新元素时都会出现内存泄漏。如何解决这个问题?经过一段时间的调查,我发现TValue.MakeWithoutCopy解决了内存泄漏的问题。感谢您的输入。然而,我认为梅森·平指出了根本问题。
TValue.Make(@ArrPointer, dynArr.Handle, ArrValue);
RField.SetValue(val.AsObject, arrValue);