深度对象比较Delphi

Bar*_*rry 13 delphi comparison object

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

Bar*_*rry 13

我自己解决这个问题,作为TObject的类助手实现,所以如果人们想要它可以在任何地方使用.由于RTTI而D2010及以上,但您可以将其转换为使用原始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.
Run Code Online (Sandbox Code Playgroud)

  • 不重要与重大差异如何?(例子:你有没有想要忽略窗口句柄值等差异的情况?你能添加一个排除属性,以便深度比较会跳过某些东西吗? (3认同)

War*_* P 5

考虑使用OmniXML持久性

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

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

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

  1. 匹配并在匹配的XML节点之间构建Object1-> Object2节点链接的映射。
  2. 对主键(域特定知识)上的每个节点进行排序,使得XML顺序不重要。由于您不仅要比较TComponents与Names,还需要找到一种建立每个对象身份的方法,以便能够进行比较。
  3. 报告xml doc 1中不在xml doc 2中的项目。
  4. 报告xml doc 2中不在xml doc 1中的项目。
  5. 报告xml doc 1中具有与xml doc2不同的子项或属性的项目。
  6. 可视工具使用了两个“虚拟树视图”控件,其工作方式与KDIFF3相似,但作为树视图。