深度对象比较 Delphi

发布于 2024-12-13 14:31:22 字数 146 浏览 2 评论 0原文

在 Delphi 中寻找一种方法来为我进行深度对象比较,最好是基于 2010 RTTI,因为我的对象不是从 TComponent 继承的。我正在 DUnit 中开发一个测试框架,需要一些可靠的东西来准确指出哪个字段导致了问题(序列化比较让它有点模糊)。

Looking for a way in Delphi to do deep object comparison for me, preferably 2010 RTTI based as my objects don't inherit from TComponent. I'm developing a test framework in DUnit and need something solid which will point out exactly which field is causing problems (serialization comparison leaves it a bit vague).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

策马西风 2024-12-20 14:31:22

我自己解决了这个问题,作为 TObject 的类助手实现,因此如果人们需要的话可以在任何地方使用。 D2010 及更高版本由于 RTTI,但您也许可以将其转换为使用原始 RTTI 内容。

下面的代码可能有错误,因为我最初是针对 DUnit 的,其中进行了大量检查而不是更改结果,并且不支持 TCollections 或大量其他特殊情况,但可以通过使用 if-elseif-then 进行调整中间切换。

如果您有任何建议和补充,请随时发表评论,以便我可以将它们添加到其中,以便其他人可以使用它。

巴里编码愉快

unit TObjectHelpers;

interface
   uses classes, rtti;

type

TObjectHelpers = class Helper for TObject
  function DeepEquals (const aObject : TObject) : boolean;
end;

implementation

uses sysutils, typinfo;

{ TObjectHelpers }

function TObjectHelpers.DeepEquals(const aObject: TObject): boolean;
var
  c : TRttiContext;
  t : TRttiType;
  p : TRttiProperty;
begin

  result := true;

  if self = aObject then
    exit; // Equal as same pointer

  if (self = nil) and (aObject = nil) then
    exit; // equal as both non instanced

  if (self = nil) and (aObject <> nil) then
  begin
    result := false;
    exit; // one nil other non nil fail
  end;

  if (self <> nil) and (aObject = nil) then
  begin
     result := false;
     exit; // one nil other non nil fail
  end;

  if self.ClassType <> aObject.ClassType then
  begin
     result := false;
     exit;
  end;

  c := TRttiContext.Create;
  try
    t := c.GetType(aObject.ClassType);

    for p in t.GetProperties do
    begin

       if ((p.GetValue(self).IsObject)) then
       begin

          if not TObject(p.GetValue(self).AsObject).DeepEquals(TObject(p.GetValue(aObject).AsObject)) then
          begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'DateTime') or AnsiSameText(p.PropertyType.Name, 'TDateTime') then
  begin

    if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
    begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'Boolean') then
  begin

    if p.GetValue(self).AsBoolean <> p.GetValue(aObject).AsBoolean then
    begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'Currency') then
  begin

     if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
     begin
        result := false;
        exit;
     end;

  end
  else if p.PropertyType.TypeKind = tkInteger then
  begin

    if p.GetValue(self).AsInteger <> p.GetValue(aObject).AsInteger then
    begin
      result := false;
      exit;
    end;

  end
  else if p.PropertyType.TypeKind = tkInt64 then
  begin

    if p.GetValue(self).AsInt64 <> p.GetValue(aObject).AsInt64  then
    begin
      result := false;
      exit;
    end;

  end
  else if p.PropertyType.TypeKind = tkEnumeration then
  begin

    if p.GetValue(self).AsOrdinal <> p.GetValue(aObject).AsOrdinal then
    begin
      result := false;
      exit;
    end;

  end
  else
  begin

    if p.GetValue(self).AsVariant <> p.GetValue(aObject).AsVariant then
    begin
      result := false;
      exit;
    end;

  end;

end;

 finally
   c.Free;
  end;

 end;

 end.

Sort of solved this myself, implemented as a class helper for TObject so can be used everywhere if people want it. D2010 and up due to RTTI but you may be able to convert it to use original RTTI stuff.

Code below may be buggy as originally mine was for DUnit and had lots of checks in it instead of changing the result and doesn't support TCollections or a load of other special cases but can be adapted for that by using the if-elseif-then switch in the middle.

If you have any suggestions and additions please don't hesitate to comment so I can add them to it so other people can use this.

Have fun coding

Barry

unit TObjectHelpers;

interface
   uses classes, rtti;

type

TObjectHelpers = class Helper for TObject
  function DeepEquals (const aObject : TObject) : boolean;
end;

implementation

uses sysutils, typinfo;

{ TObjectHelpers }

function TObjectHelpers.DeepEquals(const aObject: TObject): boolean;
var
  c : TRttiContext;
  t : TRttiType;
  p : TRttiProperty;
begin

  result := true;

  if self = aObject then
    exit; // Equal as same pointer

  if (self = nil) and (aObject = nil) then
    exit; // equal as both non instanced

  if (self = nil) and (aObject <> nil) then
  begin
    result := false;
    exit; // one nil other non nil fail
  end;

  if (self <> nil) and (aObject = nil) then
  begin
     result := false;
     exit; // one nil other non nil fail
  end;

  if self.ClassType <> aObject.ClassType then
  begin
     result := false;
     exit;
  end;

  c := TRttiContext.Create;
  try
    t := c.GetType(aObject.ClassType);

    for p in t.GetProperties do
    begin

       if ((p.GetValue(self).IsObject)) then
       begin

          if not TObject(p.GetValue(self).AsObject).DeepEquals(TObject(p.GetValue(aObject).AsObject)) then
          begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'DateTime') or AnsiSameText(p.PropertyType.Name, 'TDateTime') then
  begin

    if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
    begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'Boolean') then
  begin

    if p.GetValue(self).AsBoolean <> p.GetValue(aObject).AsBoolean then
    begin
      result := false;
      exit;
    end;

  end
  else if AnsiSameText(p.PropertyType.Name, 'Currency') then
  begin

     if p.GetValue(self).AsExtended <> p.GetValue(aObject).AsExtended then
     begin
        result := false;
        exit;
     end;

  end
  else if p.PropertyType.TypeKind = tkInteger then
  begin

    if p.GetValue(self).AsInteger <> p.GetValue(aObject).AsInteger then
    begin
      result := false;
      exit;
    end;

  end
  else if p.PropertyType.TypeKind = tkInt64 then
  begin

    if p.GetValue(self).AsInt64 <> p.GetValue(aObject).AsInt64  then
    begin
      result := false;
      exit;
    end;

  end
  else if p.PropertyType.TypeKind = tkEnumeration then
  begin

    if p.GetValue(self).AsOrdinal <> p.GetValue(aObject).AsOrdinal then
    begin
      result := false;
      exit;
    end;

  end
  else
  begin

    if p.GetValue(self).AsVariant <> p.GetValue(aObject).AsVariant then
    begin
      result := false;
      exit;
    end;

  end;

end;

 finally
   c.Free;
  end;

 end;

 end.
可是我不能没有你 2024-12-20 14:31:22

考虑使用 OmniXML 持久性

对于 XML 差异,我使用 OmniXML 编写了一个实用程序来执行 XML 差异,并且有许多 XML 比较工具。

我使用 OmniXML 制作了一个 XML 差异工具来实现这个目的,它对我来说非常有用。不幸的是,该工具包含许多特定于领域的内容,并且是闭源的,属于前雇主,因此我无法发布代码。

我的比较工具有一个简单的算法:

  1. 匹配并构建匹配 XML 节点之间的 Object1->Object2 节点链接的映射。
  2. 根据主键(特定于领域的知识)对每个节点进行排序,从而使 XML 顺序变得不重要。由于您不仅将 TComponent 与名称进行比较,因此如果您希望能够进行比较,您将需要找到一种方法来建立每个对象的标识。
  3. 报告 xml 文档 1 中不在 xml 文档 2 中的项目。
  4. 报告 xml 文档 2 中不在 xml 文档 1 中的项目。
  5. 报告 xml 文档 1 中子项或属性与 xml doc2 不同的项目。
  6. 可视化工具使用两个虚拟树视图控件,其工作方式与 KDIFF3 非常相似,但作为树视图。

Consider using OmniXML persistence.

For XML differencing, I have written a utility using OmniXML that will do an XML diff, and there are many XML comparison tools out there.

I used OmniXML to do an XML differencing tool for exactly this purpose, and it worked great for me. Unfortunately that tool contains many domain specific things and is closed-source and belongs to a former employer so I cannot post the code.

My comparision tool had a simple algorithm:

  1. Match and build a map of Object1->Object2 node links between matching XML nodes.
  2. Sort every node on a primary key (domain specific knowledge) making XML order unimportant. Since you are not only comparing TComponents with Names, you will need to find a way to establish every objects identity if you want to be able to compare it.
  3. Report items in xml doc 1 that are not in xml doc 2.
  4. Report items in xml doc 2 that are not in xml doc 1.
  5. Report items in xml doc 1 with subkeys or attributes different than xml doc2.
  6. visual tool used two Virtual Tree View controls, and worked a lot like KDIFF3 but as a treeview.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文