为什么我不能从 RTTI 获得 IList 的“添加”和“清除”方法

Why can't I get the 'Add' and 'Clear' methode of IList from RTTI

提问人:Ravaut123 提问时间:11/17/2023 最后编辑:Remy LebeauRavaut123 更新时间:11/17/2023 访问量:89

问:

我有一个接口(来自 ),我想用MVCFrameWork反序列化。它不起作用,因为在代码中我们有一个函数,用于查看方法是否存在,但 RTTI 不包括这些方法。IList<T>Spring.CollectionsIsWrap()Add()Clear()IList

一些代码:

TPerson = class (TInterfacedObject)
private
  fLastName: String;
  fFirstName: String;
public
  property FirstName: String read fFirstName write fFirstName;
  property LastName: String read fLastName write fLastName;
end;

implementation

uses
  Generics.Collections,
  Spring.Collections,
  MVCFramework.Serializer.Intf,
  MVCFramework.Serializer.JsonDataObjects;

procedure TSerializerTest.JsonToCollection;
begin
  var strJson: string := '[{"FirstName": "Ricardo", "LastName": "The killer" }, {"FirstName": "Zorro", "LastName": "Z-Man" }]';
  var oPersons : IList<TPerson> := TCollections.CreateObjectList<TPerson> ;
  //  var oPersons : TList<TPerson> := TList<TPerson>.Create ;
  //  var oPersons : TObjectList<TPerson> := TObjectList<TPerson>.Create() ;
  var lSerializer: IMVCSerializer := TMVCJsonDataObjectsSerializer.Create;

  lSerializer.DeserializeCollection(strJson, oPersons, TPerson);

  Assert.AreEqual(oPersons[0].FirstName , 'Ricardo');
  Assert.AreEqual(oPersons[1].FirstName , 'Zorro');
end;

TList<TPerson>和工作。TObjectList<TPerson>

IList<TPerson>不起作用,因为我们找不到方法和 RTTI。Add()Clear()

procedure TSerializerTest.CheckAddAndClearRTTI;
var
  ObjectType: TRttiType;
  FContext: TRttiContext;
begin 
  var oPersons : IList<TPerson> := TCollections.CreateObjectList<TPerson> ;

  //METHODE ADD AND CLEAR EXIST 
  oPersons.Add(TPerson.Create);  
  oPersons.Clear;

  ObjectType := FContext.GetType(oPersons.AsObject.ClassInfo);
  if (ObjectType.GetMethod('Add') <> nil) and (ObjectType.GetMethod('Clear') <> nil)  then
  begin 
    ShowMessage('Is List');  // DOES NOT GET HERE, WHY
  end;
end;
Delphi 反序列化 Spring4D

评论

1赞 Remy Lebeau 11/17/2023
您不是在访问本身的 RTTI,而是在访问支持 的对象的 RTTI。我不知道 Spring 是如何实现的,但我的猜测是支持对象根本没有这些方法可用,它可能将它们映射到不同的名称(参见方法解析子句)。您是否尝试使用对象的 RTTI 来枚举它的实际方法?IListIList

答:

3赞 Stefan Glienke 11/17/2023 #1

我假设您使用的是 Spring4D 2.0,因为在 1.2 中,继承的接口默认为它们提供方法 RTTI。IInvokable

但是,在 2.0 中,这种情况发生了变化,接口和支持它们的对象都不会发出任何增强的 RTTI,因为接口不再继承或添加到它们,并且在实现类中,所有增强的 RTTI 都通过子句关闭。这是设计使然,以避免导致大量不必要的二进制膨胀。IInvokable{$M+}$RTTI

知道在集合上使用 RTTI 的主要用途之一是序列化,Spring4D 提供了另一种方法:

列表和许多其他集合类型支持非泛型接口,从该接口内部创建围绕泛型集合的包装对象,并公开 和 和 方法的功能。ICollectionSpring.CollectionsIEnumerableAdd(const value: TValue)Clear

var persons := TCollections.CreateObjectList<TPerson>;

var coll := persons as ICollection;
coll.Add(TPerson.Create);
coll.Clear;

我快速研究了 DMVC 代码,我可以说它们处理接口的方式很糟糕,只需将它们转换为对象并将其传递下来。我不知道他们是否有一些自定义序列化的扩展点,但这需要修复。

0赞 Ravaut123 11/17/2023 #2

感谢 Stefan 和 Remy。

在DMVC中,我可以进行自定义序列化

  TListSerializer = class(TInterfacedObject, IMVCTypeSerializer)
  public
    procedure SerializeAttribute(const AElementValue: TValue; const APropertyName: string;const ASerializerObject: TObject; const AAttributes: System.TArray<System.TCustomAttribute>);
    procedure SerializeRoot(const AObject: TObject; out ASerializerObject: TObject;const AAttributes: System.TArray<System.TCustomAttribute>;const ASerializationAction: TMVCSerializationAction = nil);
    procedure DeserializeAttribute(var AElementValue: TValue; const APropertyName: string; const ASerializerObject: TObject; const AAttributes: System.TArray<System.TCustomAttribute>);
    procedure DeserializeRoot(const ASerializerObject: TObject; const AObject: TObject;const AAttributes: System.TArray<System.TCustomAttribute>);
  end;

我注册了序列化程序

  var lSerializer: IMVCSerializer := TMVCJsonDataObjectsSerializer.Create;
  lSerializer.RegisterTypeSerializer(TypeInfo(TFoldedList<System.TObject>), TListSerializer.Create);

IList<TPerson>是一个TFoldedList<System.TObject>

评论

1赞 Stefan Glienke 11/17/2023
它现在有效,这对您有好处,但这种方法只是要求下次内部发生更改时失败 - 它们的序列化机制实际上不应该在接口后面的对象上工作,而应该在接口本身上工作。