添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'd like to set the length of a dynamic array, as suggested in this post . I have two classes TMyClass and the related TChildClass defined as

TChildClass = class
private
  FField1:  string;
  FField2:  string;
TMyClass = class
private
  FField1:  TChildClass;
  FField2:  Array of TChildClass;

The array augmentation is implemented as

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; 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; ReadLn; Val.AsObject.Free; finally RContext.Free;

Being new to D2010 RTTI, I suspected the error could depend on getting ArrValue from the class instance, but the subsequent WriteLn prints "TRUE", so I've ruled that out. Disappointingly, however, the same WriteLn reports that the size of ArrValue is 0, which is confirmed by the "Index out of bounds"-exception I get when trying to set any of the elements in the array (through ArrValue.SetArrayElement(i, Value);). Do anyone know what I'm doing wrong here? (Or perhaps there is a better way to do this?) TIA!

Dynamic arrays are kind of tricky to work with. They're reference counted, and the following comment inside DynArraySetLength should shed some light on the problem:

// If the heap object isn't shared (ref count = 1), just resize it. Otherwise, we make a copy

Your object is holding one reference to it, and so is the TValue. Also, GetReferenceToRawData gives you a pointer to the array. You need to say PPointer(GetReferenceToRawData)^ to get the actual array to pass to DynArraySetLength.

Once you've got that, you can resize it, but you're left with a copy. Then you have to set it back onto the original array.

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

All in all, it's probably a lot simpler to just use a list instead of an array. With D2010 you've got Generics.Collections available, which means you can make a TList<TChildClass> or TObjectList<TChildClass> and have all the benefits of a list class without losing type safety.

Thanks Mason, I guess I should've investigated that a little closer before asking... Anyways; I'm feeling pretty lazy lately, and TList and/or TObjectList is more convenient so I'll give it a spin. :) +1 – conciliator Mar 29, 2010 at 6:41 I just needed the same and it works fine, but I get a memory leak each time I add a new element. Some ideas how to resolve this? – Christian Metzler Mar 22, 2011 at 10:27 After some time of investigation, I found out that TValue.MakeWithoutCopy solves the problem of memory leaking. – Christian Metzler Mar 22, 2011 at 15:38

and use that.

From an old RTTI based XML serializer I know the general method that you use should work (D7..2009 tested):

procedure TXMLImpl.ReadArray(const Name: string; TypeInfo: TArrayInformation; Data: Pointer; IO: TParameterInputOutput);
  P: PChar;
  L, D: Integer;
  BT: TTypeInformation;
begin
  FArrayType := '';
  FArraySize := -1;
  ComplexTypePrefix(Name, '');
    // Get the element type info.
    BT := TypeInfo.BaseType;
    if not Assigned(BT) then RaiseSerializationReadError; // Not a supported datatype!
    // Typecheck the array specifier.
    if (FArrayType <> '') and (FArrayType <> GetTypeName(BT)) then RaiseSerializationReadError;
    // Do we have a fixed size array or a dynamically sized array?
    L := FArraySize;
    if L >= 0 then begin
      // Set the array
      DynArraySetLength(PPointer(Data)^,TypeInfo.TypeInformation,1,@L);
      // And restore he elements
      D := TypeInfo.ElementSize;
      P := PPointer(Data)^;
      while L > 0 do begin
        ReadElement(''{ArrayItemName},BT,P,IO); // we allow any array item name.
        Inc(P,D);
        Dec(L);
    end else begin
      RaiseNotSupported;
  finally
    ComplexTypePostfix;

Hope this helps..

Thanks for the input Ritsaert. However, I think Mason pin-pointed the underlying problem. – conciliator Mar 29, 2010 at 6:43

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.