3

この投稿で提案されているように、動的配列の長さを設定したいと思います。2つのクラス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!

4

2 に答える 2

8

動的配列は扱いが難しいものです。それらは参照カウントであり、DynArraySetLength内の次のコメントは、問題にいくらかの光を当てるはずです。

//ヒープオブジェクトが共有されていない場合(ref count = 1)、サイズを変更するだけです。それ以外の場合は、コピーを作成します

オブジェクトはそれへの1つの参照を保持しており、TValueも同様です。また、GetReferenceToRawDataは、配列へのポインターを提供します。PPointer(GetReferenceToRawData)^DynArraySetLengthに渡す実際の配列を取得するように言う必要があります。

それを取得したら、サイズを変更できますが、コピーが残ります。次に、元のアレイに戻す必要があります。

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

全体として、配列の代わりにリストを使用する方がおそらくはるかに簡単です。D2010を使用すると、Generics.Collectionsを利用できるようになります。つまり、型の安全性を失うことなく、TList<TChildClass>またはTObjectList<TChildClass>を作成して、リストクラスのすべての利点を活用できます。

于 2010-03-27T15:39:50.123 に答える
0

配列を別の型として定義する必要があると思います。

TMyArray = array of TMyClass;

そしてそれを使用します。

古いRTTIベースのXMLシリアライザーから、使用する一般的な方法が機能するはずです(D7..2009テスト済み):

procedure TXMLImpl.ReadArray(const Name: string; TypeInfo: TArrayInformation; Data: Pointer; IO: TParameterInputOutput);
var
  P: PChar;
  L, D: Integer;
  BT: TTypeInformation;
begin
  FArrayType := '';
  FArraySize := -1;
  ComplexTypePrefix(Name, '');
  try
    // 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;
    end else begin
      RaiseNotSupported;
    end;
  finally
    ComplexTypePostfix;
  end;
end;

お役に立てれば..

于 2010-03-27T10:06:26.080 に答える