如何使用Delphi 2010 RTTI设置数组长度

4
如何在运行时设置数组长度? setLength(t.GetProperty('Propertys'),3); ????
 unit Unit3;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;

    type

    TSubProperty = Class
      private
        Fitem2: Integer;
        Fitem1: String;
        procedure Setitem1(const Value: String);
        procedure Setitem2(const Value: Integer);
      published
      property item1:String read Fitem1 write Setitem1;
      property item2:Integer read Fitem2 write Setitem2;
    End;

    TArraySubPropertys=array of TSubProperty;

    TmyObject = Class
      private
        FPropertys: TArraySubPropertys;
        procedure SetPropertys(const Value: TArraySubPropertys);
      published
      property Propertys:TArraySubPropertys read FPropertys write SetPropertys;
    End;


      TForm3 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    var
      Form3: TForm3;

    implementation

    {$R *.dfm}

    procedure TForm3.Button1Click(Sender: TObject);
    var
    myObject:TmyObject;
    ctx : TRttiContext;
    t : TRttiType;
    obj:TObject;
    begin
    myObject :=TmyObject.Create;
    ctx := TRttiContext.Create;
    t := ctx.GetType(myObject.ClassType);
   // setLength(t.GetProperty('Propertys'),3); ????????????????????????????????????
   obj:= (t.GetProperty('Propertys').PropertyType as TRttiDynamicArrayType).ElementType.AsInstance.MetaclassType.Create;
  //showmessage(obj.toStirng); --> TSubProperty 
   t.GetProperty('Propertys').getValue(myObject).setArrayElement(0,obj);

   obj:= (t.GetProperty('Propertys').PropertyType as TRttiDynamicArrayType).ElementType.AsInstance.MetaclassType.Create;
   t.GetProperty('Propertys').getValue(myObject).setArrayElement(1,obj);

   obj:= (t.GetProperty('Propertys').PropertyType as TRttiDynamicArrayType).ElementType.AsInstance.MetaclassType.Create;
   t.GetProperty('Propertys').getValue(myObject).setArrayElement(2,obj);

{
    myObject.Propertys[0] :=TSubProperty.Create;
    myObject.Propertys[0].item1 :='x';
    myObject.Propertys[0].item2 :=1;

    myObject.Propertys[1] :=TSubProperty.Create;
    myObject.Propertys[1].item1 :='y';
    myObject.Propertys[1].item2 :=2;


    myObject.Propertys[2] :=TSubProperty.Create;
    myObject.Propertys[2].item1 :='z';
    myObject.Propertys[2].item2 :=3;


    ShowMessage(myObject.Propertys[2].item1);

    FreeAndNil(myObject.Propertys[2]);
    FreeAndNil(myObject.Propertys[1]);
    FreeAndNil(myObject.Propertys[0]);
}
    FreeAndNil(myObject);

    end;

    { TSubProperty }

    procedure TSubProperty.Setitem1(const Value: String);
    begin
      Fitem1 := Value;
    end;

    procedure TSubProperty.Setitem2(const Value: Integer);
    begin
      Fitem2 := Value;
    end;

    { TmyObject }

    procedure TmyObject.SetPropertys(const Value: TArraySubPropertys);
    begin
      FPropertys := Value;
    end;

    end.

编辑:

这段代码改变了数组的长度,但是有误。它使用的是一个随机值而不是5。(19736192)

procedure TForm3.Button3Click(Sender: TObject);
var
myObject:TmyObject;
ctx : TRttiContext;
t : TRttiType;
v:TValue;
len:Longint;
p:pointer;
begin
   myObject :=TmyObject.Create;
   ctx := TRttiContext.Create;
   t := ctx.GetType(myObject.ClassType);
   V := t.GetField('FPropertys').GetValue(myObject);
   len:=5;
   p:=v.GetReferenceToRawData;
   ShowMessage(inttostr(integer(@myObject.FPropertys))); //19795652
   ShowMessage(inttostr(integer(p)));                    //19795672
   DynArraySetLength(p,v.TypeInfo,1,@len);
   t.GetField('FPropertys').SetValue(myObject,v);
   ShowMessage(inttostr(length(myObject.Propertys)));    //array length=19736192 ???
end;

编辑2:

@Robert Love,谢谢您的回复,但问题仍然存在。 (Embarcadero® Delphi® 2010版本14.0.3513.24210)

单击button2时,出现无效指针操作。

procedure TForm7.Button2Click(Sender: TObject);
var
myObject:TmyObject;
ctx : TRttiContext;
t   : TRttiType;
v   : Tvalue;
p   : Pointer;
Len : Longint;
begin
  myObject :=TmyObject.Create;
  ctx := TRttiContext.Create;
  t   := ctx.GetType(myobject.ClassType);
  V := t.GetProperty('Propertys');
  Len := 3;
  P := V.GetReferenceToRawData;
  DynArraySetLength(P,V.TypeInfo,1,@Len); // error invalid pointer operation
  ShowMessage(inttostr(length(myObject.Propertys)));
end;

请注意,property 的复数形式是 properties,而不是 propertys。拼写在源代码中也很重要。 - Rob Kennedy
@Rob Kennedy,你是对的。第三方定义的类。 - user215424
1个回答

6
您可以使用DynArraySetLength函数。
var
 ...
 V : TValue;
 Len : LongInt;
 P : Pointer;
begin
  ...
  V := t.GetProperty('Propertys');
  Len := 3;
  P := V.GetReferenceToRawData;
  DynArraySetLength(P,V.TypeInfo,1,@Len);
  ...
end;

我在TArrayElementAdd类中使用这种方法,具体实现可以参考RttiUtils.pas


@Robert Love,DynArraySetLength(V,V.TypeInfo,1,@Len); 不起作用。 :( E2033 实际和形式变量参数的类型必须相同。 - user215424
1
我已经解决了问题,在回复时有点太困了。 - Robert Love
2
根据RTTI动态数组TValue Delphi 2010,正确的答案应该是DynArraySetLength(PPointer(V.GetReferenceToRawData)^,V.TypeInfo,1,@Len); - LU RD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接