2011-11-03 19 views
11

Buscando una biblioteca delphi o un fragmento de código que haga una comparación de objetos profunda para mí, preferiblemente RTTI basado en 2010 ya que mis objetos no heredan de TComponent. ¿Alguien sabe dónde puedo encontrar uno, estoy desarrollando un marco de prueba en DUnit y necesito algo sólido que indique exactamente qué campo está causando problemas (la comparación de serialización lo deja un poco vago).Comparación de objetos profundos Delphi

Saludos,

Barry

+5

serialización a un formato de texto (XML, JSON) y haciendo un TDiff (http://angusj.com/delphi/) sería fácil – mjn

+1

¿Qué tal Formato DFM :-) Es como un precursor JSON ... de 1995. –

Respuesta

12

Esto lo solucioné yo mismo, implementado como un ayudante de clase para TObject, por lo que se puede usar en todas partes si la gente lo quiere. D2010 y más debido a RTTI, pero es posible que puedas convertirlo para usar material RTTI original.

El siguiente código puede tener errores ya que originalmente el mío era para DUnit y tenía muchos controles en lugar de cambiar el resultado y no es compatible con TCollections o una carga de otros casos especiales, pero se puede adaptar utilizando -elseif-luego cambia en el medio.

Si tiene alguna sugerencia e información adicional, no dude en hacer un comentario para que yo pueda agregarla y otras personas puedan usarla.

Diviértete codificación

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. 
+3

¿Qué hay de las diferencias no significativas frente a las significativas? (Ejemplos: ¿alguna vez tendrías un caso en el que quisieras ignorar diferencias como los valores del identificador de ventana, etc.? ¿Podrías agregar un atributo de exclusión para que la comparación profunda omita algunas cosas? –

+1

Se rompe cuando alguien hace redefiniciones de tipo para los tipos que identifique por nombre. Más bien debería comparar TypeInfo de estos tipos. –

+0

O use NOWHERE si las personas ya están usando su propia (o la de otra persona) ayudante de clase para TObject. Para gritar en voz alta, ¿cuándo DEJARÁN las personas usar ayudantes de clase para cosas que las funciones/procedimientos a nivel de unidad son lo suficientemente buenas y más que razonables. Realmente parece como si la gente estuviera tan desesperada por encontrar un uso legítimo para los ayudantes de clase que los lanzarían a todos los problemas, ya que el 99% de ellos son totalmente inadecuado. – Deltics

4

Considere el uso de OmniXML persistence.

Para la diferenciación XML, he escrito una utilidad que usa OmniXML que hará una diferencia XML, y hay muchas herramientas de comparación XML.

Utilicé OmniXML para hacer una herramienta de diferenciación XML para este propósito, y funcionó muy bien para mí. Desafortunadamente esa herramienta contiene muchas cosas específicas del dominio y es de código cerrado y pertenece a un antiguo empleador, por lo que no puedo publicar el código.

Mi herramienta de comparación tenía un algoritmo simple:

  1. Partido y construir un mapa de enlaces Object1-> Object2 nodo entre los nodos XML coincidente.
  2. Ordene cada nodo en una clave principal (conocimiento específico del dominio) haciendo que el orden XML no sea importante. Ya que no solo comparas TComponents con Names, necesitarás encontrar la forma de establecer la identidad de cada objeto si deseas poder compararlo.
  3. Los elementos de informe en XML doc 1 que no están en el documento XML 2.
  4. Los elementos de informe en XML doc 2 que no están en el documento XML 1.
  5. Los elementos de informe en XML doc 1 con subclaves o atributos diferentes a xml doc2.
  6. herramienta visual utiliza dos controles de vista de árbol virtual, y funcionaba mucho como KDIFF3 pero como una vista de árbol.
+0

Esa es una buena solución, pero aún un poco demasiado alta y compleja para mi gusto, especialmente cuando se utiliza en una prueba DUnit. Creo que acabo de escribir algo que hará el trabajo Lo publicaré en un bit – Barry

+1

Es exactamente cómo lo usé; DUnit.Creó una gran cantidad de archivos de resultados de pruebas unitarias en el disco, que era exactamente lo que necesitaba para encontrar, comprender y corregir las regresiones que estaban sucediendo. Los datos son poder –

+0

ah bastante, solo diferentes estilos realmente. El conjunto de pruebas debe ser utilizado por algunas personas, por lo que sería más obvio para el programador que está trabajando en él antes que tener que revisar los archivos y verificar. – Barry

Cuestiones relacionadas