Глубокое сравнение объектов Delphi - PullRequest
12 голосов
/ 03 ноября 2011

Ищу способ сделать в Delphi глубокое сравнение для меня, предпочтительно на основе RTTI 2010, поскольку мои объекты не наследуются от TComponent.Я разрабатываю тестовый фреймворк в DUnit и мне нужно что-то солидное, в котором будет указано, какое именно поле вызывает проблемы (сравнение сериализации оставляет его немного расплывчатым).

Ответы [ 2 ]

12 голосов
/ 03 ноября 2011

Вроде как решил сам, реализован как помощник класса для 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.
4 голосов
/ 03 ноября 2011

Рассмотрите возможность использования Постоянство OmniXML .

Для различий в XML я написал утилиту, использующую OmniXML, которая будет выполнять различие в XML, и существует множество инструментов сравнения XML.

Я использовал OmniXML для создания инструмента XML-различий именно для этой цели, и он отлично работал для меня.К сожалению, этот инструмент содержит множество специфичных для предметной области вещей, имеет закрытый исходный код и принадлежит бывшему работодателю, поэтому я не могу опубликовать код.

Мой инструмент сравнения имел простой алгоритм:

  1. Соответствиеи построить карту связей узлов Object1-> Object2 между соответствующими узлами XML.
  2. Сортировать каждый узел по первичному ключу (знание предметной области), делая порядок XML неважным.Поскольку вы не только сравниваете TComponents с именами, вам нужно будет найти способ установить идентичность каждого объекта, если вы хотите иметь возможность сравнивать его.
  3. Элементы отчета в xml doc 1, которых нет в xml doc2.
  4. Элементы отчета в xml doc 2, которых нет в xml doc 1.
  5. Элементы отчета в xml doc 1 с подразделами или атрибутами, отличными от xml doc2.
  6. visualИнструмент использовал два элемента управления Virtual Tree View и работал, как KDIFF3, но в виде дерева.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...