深度对象比较Delphi

时间:2011-11-03 11:41:03

标签: delphi object comparison

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

2 个答案:

答案 0 :(得分:12)

我自己解决这个问题,作为TObject的类助手实现,所以如果人们想要它可以在任何地方使用。由于RTTI而D2010及以上,但您可以将其转换为使用原始RTTI内容。

下面的代码可能是错误的,因为我最初是为了DUnit并且在其中进行了大量检查而不是更改结果并且不支持TCollections或其他特殊情况的加载但是可以通过使用if-来适应它elseif-然后在中间切换。

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

享受有趣的编码

巴里

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.

答案 1 :(得分:4)

考虑使用OmniXML persistence

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

我使用OmniXML来完成这个目的的XML差异工具,它对我来说很有用。不幸的是,该工具包含许多特定于域的内容,并且是封闭源代码,属于前雇主,因此我无法发布代码。

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

  1. 在匹配的XML节点之间匹配并构建Object1-&gt; Object2节点链接的映射。
  2. 对主键上的每个节点(特定于域的知识)进行排序,使XML顺序不重要。由于您不仅要将TComponents与Names进行比较,还需要找到一种方法来建立每个对象标识,如果您希望能够比较它。
  3. 报告xml doc 1中不在xml doc 2中的项目。
  4. 报告xml doc 2中不在xml doc 1中的项目。
  5. 使用不同于xml doc2的子项或属性在xml doc 1中报告项目。
  6. 可视化工具使用了两个虚拟树视图控件,并且像KDIFF3一样工作但是作为树视图。