Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度? [英] Delphi: how to set the length of a RTTI-accessed dynamic array using DynArraySetLength?

查看:409
本文介绍了Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想设置动态数组的长度,如这篇文章。我有两个类TMyClass和相关的TChildClass定义为

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;
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,这是通过Index out of bounds确认的 - 在尝试设置任何数组中的元素(通过 ArrValue.SetArrayElement(i,Value); )。有人知道我在这里做错了吗? (或者也许有更好的方法来做这个?)TIA!

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!

推荐答案

动态数组是一件很棘手的工作。他们是引用计数,DynArraySetLength中的以下注释应该解释问题:

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:

//如果堆对象不共享(ref count = 1 ),只是调整大小。否则,我们复制一个副本

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

您的对象持有一个引用,而TValue也是这样。另外,GetReferenceToRawData给你一个指向数组的指针。您需要说 PPointer(GetReferenceToRawData)^ 以获取实际数组传递给DynArraySetLength。

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);

总而言之,使用列表而不是数组可能要简单得多。使用D2010,您可以使用Generics.Collections,这意味着您可以使用 TList< TChildClass> TObjectList< TChildClass> 并具有列表类的所有好处,而不会失去类型安全性。

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.

这篇关于Delphi:如何使用DynArraySetLength设置RTTI访问的动态数组的长度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆