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

查看:524
本文介绍了德尔福:如何设置使用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;

阵列增强被实现为

The array augmentation is implemented as

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,这是确认的索引出界-exception我在尝试设置任何时候该数组中的元素(通过 ArrValue.SetArrayElement(I,值); )。难道有谁知道我在做什么错在这里? (也许有更好的方法来做到这一点?)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:

//如果堆对象不是共享的(引用计数= 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.

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

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