如何将 TObject 转换为 TObjectList<T>?

How do I cast a TObject as a TObjectList<T>?

我有一个程序需要将 TObject 的数组插入到列表中。该列表可以是任何受支持的类型,例如TObjectListTObjectList<T>TROArray

程序如下所示:

type
  TObjectArray = Array of TObject;

...

procedure TMyClass.DoAssignObjectList(const ObjectArray: TObjectArray;
  const DstList: TObject);
var
  i: Integer;
begin
  if DstList is TObjectList then
  begin
    for i := 0 to pred(TObjectList(DstList).Count) do
      TObjectList(DstList).Add(ObjectArray[i]);
  end else
  if DstList is TObjectList<T> then // Obviously this doesn't work
  begin
    for i := 0 to pred(TObjectList<T>(DstList).Count) do
      TObjectList<T>(DstList).Add(ObjectArray[i]);
  end
  else
  begin
    raise Exception.CreateFmt(StrNoDoAssignORMObject, [DstList.ClassName]);
  end;
end;

如何检查对象是否为 TObjectList<T>,然后向其添加数组元素?

在我写这个问题的时候,我想出了一种使用 RTTI 来做到这一点的方法。它应该适用于任何具有过程 Add(AObject: TObject) 的列表。

procedure TransferArrayItems(const Instance: TObject;
  const ObjectArray: TObjectArray);
const
  AddMethodName = 'Add';
var
  Found: Boolean;
  LMethod: TRttiMethod;
  LIndex: Integer;
  LParams: TArray<TRttiParameter>;
  i: Integer;
  RTTIContext: TRttiContext;
  RttiType: TRttiType;
begin
  Found := False;
  LMethod := nil;

  if length(ObjectArray) > 0 then
  begin
    RTTIContext := TRttiContext.Create;
    RttiType := RTTIContext.GetType(Instance.ClassInfo);

    for LMethod in RttiType.GetMethods do
    begin
      if SameText(LMethod.Name, AddMethodName) then
      begin
        LParams := LMethod.GetParameters;

        if length(LParams) = 1 then
        begin
          Found := TRUE;

          for LIndex := 0 to length(LParams) - 1 do
          begin
            if LParams[LIndex].ParamType.Handle <> TValue(ObjectArray[0]).TypeInfo
            then
            begin
              Found := False;

              Break;
            end;
          end;
        end;

        if Found then
          Break;
      end;
    end;

    if Found then
    begin
      for i := Low(ObjectArray) to High(ObjectArray) do
      begin
        LMethod.Invoke(Instance, [ObjectArray[i]]);
      end;
    end
    else
    begin
      raise Exception.CreateFmt(StrMethodSNotFound, [AddMethodName]);
    end;
  end;
end;

您必须使用一点 RTTI 来获取有关泛型类型的更多信息。

以下代码使用了 Spring4D,它有一些方法:

uses 
  ...
  Spring.Reflection;

procedure DoAssignObjectList(const ObjectArray: TObjectArray;
  const DstList: TObject);

  function IsGenericTObjectList(const obj: TObject): Boolean;
  var
    t: TRttiType;
  begin
    t := TType.GetType(obj.ClassInfo);
    Result := t.IsGenericType and (t.GetGenericTypeDefinition = 'TObjectList<>');
  end;

begin
  ...
  if IsGenericTObjectList(DstList) then
  begin
    for i := 0 to pred(TObjectList<TObject>(DstList).Count) do
      TObjectList<TObject>(DstList).Add(ObjectArray[i]);
  ...
end;

除此之外,您还可以获得有关列表的通用参数类型的信息,以检查您放入其中的对象是否符合要求(当然只适用于通用类型):

function GetGenericTObjectListParameter(const obj: TObject): TClass;
var
  t: TRttiType;
begin
  t := TType.GetType(obj.ClassInfo);
  Result := t.GetGenericArguments[0].AsInstance.MetaclassType;
end;