我想设置动态数组的长度,如建议的那样这个帖子。我有两个类 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);
)。有谁知道我在这里做错了什么? (或者也许有更好的方法来做到这一点?)TIA!
动态数组使用起来有点棘手。它们是引用计数的,DynArraySetLength 中的以下注释应该可以阐明该问题:
// 如果堆对象不是共享的(引用计数 = 1),只需调整它的大小。否则,我们会复制一份
您的对象持有对其的一个引用,TValue 也是如此。此外,GetReferenceToRawData 还为您提供了指向数组的指针。你需要说PPointer(GetReferenceToRawData)^
获取要传递给 DynArraySetLength 的实际数组。
一旦你得到了它,你可以调整它的大小,但你留下了一个副本。然后你必须将其设置回原始数组。
TValue.Make(@ArrPointer, dynArr.Handle, ArrValue);
RField.SetValue(val.AsObject, arrValue);
总而言之,仅使用列表而不是数组可能要简单得多。使用 D2010,您可以使用 Generics.Collections,这意味着您可以制作TList<TChildClass>
or TObjectList<TChildClass>
并具有列表类的所有优点,而又不失去类型安全性。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)