2011-11-03 26 views
11

Poszukuję biblioteki delphi lub fragmentu kodu, który wykona dla mnie głębokie porównanie obiektów, najlepiej w wersji 2010 opartej na RTTI, ponieważ moje obiekty nie dziedziczą z TComponent. Czy ktoś wie, gdzie mogę go znaleźć, rozwijam framework testowy w DUnit i potrzebuję czegoś solidnego, które wskaże dokładnie, które pole powoduje problemy (porównanie serializacji pozostawia nieco niejasne).Porównanie głębokich obiektów Delphi

Cheers,

Barry

+5

serializacji do formatu tekstowego (XML, JSON) i robi Tdiff (http://angusj.com/delphi/) byłby łatwy – mjn

+1

Jak o Format DFM :-) To jest jak prekursor JSON ... od 1995. –

Odpowiedz

12

Sort to rozwiązany, zaimplementowany jako pomocnik klasy dla TObject, więc może być używany wszędzie, jeśli ludzie tego chcą. D2010 i nowszy z powodu RTTI, ale możesz go przekonwertować, aby używał oryginalnych rzeczy RTTI.

kod poniżej może być wadliwy jak pierwotnie mój był dla DUnit i miał wiele kontroli w nim zamiast zmieniając wynik i nie obsługuje TCollections lub ładunek innych szczególnych przypadkach, ale może być dostosowany do tego za pomocą if -elseif-następnie przełącz się w środku.

Jeśli masz jakieś sugestie i uzupełnienia, nie wahaj się, aby je skomentować, więc mogę dodać je do tego, aby inni mogli z tego korzystać.

Baw kodowania

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

Co powiesz na nieistotne i znaczące różnice? (Przykłady: czy kiedykolwiek miałbyś przypadek, w którym chcesz zignorować różnice, takie jak wartości uchwytów okna, itp. Czy mógłbyś dodać atrybut wykluczający, aby głębokie porównanie pomijało pewne rzeczy? –

+1

To łamie się, gdy ktoś wprowadza redefinicje typów dla typów, Identyfikuj po imieniu, raczej powinieneś porównać TypeInfo tych typów: –

+0

Lub użyć NOWHERE, jeśli ludzie używają już własnego (lub cudzego) pomocnika klasowego dla TObject.A krzyczeć głośno, kiedy ludzie przestaną używać pomocy klasy dla rzeczy, które Funkcje/procedury na poziomie jednostki są wystarczająco dobre i bardziej niż uzasadnione! Naprawdę wygląda na to, że ludzie tak desperacko szukają uzasadnionego użycia dla pomocników klasy, że rzucają je w każdym problemie - w 99% z nich są całkowicie nieodpowiedni, – Deltics

4

Rozważ użycie OmniXML persistence.

Dla różnicowania XML, napisałem narzędzie, używając OmniXML, które zrobi XML diff, i istnieje wiele narzędzi porównawczych XML tam.

Użyłem OmniXML do zrobienia narzędzia różnicującego XML dla dokładnie tego celu, i zadziałało to dla mnie. Niestety to narzędzie zawiera wiele rzeczy związanych z domeną i jest zamknięte-źródło i należy do byłego pracodawcy, więc nie mogę opublikować kodu.

Moje narzędzie Porównanie miał prosty algorytm:

  1. mecz i zbudować mapę powiązań Object1-> object2 węzłów między węzłami dopasowanie XML.
  2. Sortowanie każdego węzła na kluczu podstawowym (wiedza specyficzna dla danej domeny) sprawia, że ​​kolejność XML jest nieistotna. Ponieważ nie tylko porównujesz TComponents z Names, musisz znaleźć sposób na ustalenie każdej tożsamości obiektów, jeśli chcesz móc je porównać.
  3. Elementy raportu w formacie XML doc 1, które nie są w xml doc 2.
  4. Elementy raportu w formacie XML doc 2, które nie są w xml doc 1.
  5. Elementy raportu w formacie XML doc 1 z podkluczach lub atrybutów inny niż xml doc2.
  6. Narzędzie wizualne wykorzystało dwie kontrolki Virtual Tree View i działało bardzo podobnie do KDIFF3, ale jako widok drzewa.
+0

To jest dobre rozwiązanie, ale wciąż nieco zbyt napowietrzne i skomplikowane jak na mój gust, szczególnie gdy jest używany w teście DUnit. Wydaje mi się, że właśnie napisałem coś, co wykona zadanie, a ja opublikuję go w nieco – Barry

+1

. Dokładnie to użyłem; DUnit.Stworzyło wiele plików wyjściowych testu jednostkowego na dysku, co było dokładnie tym, czego potrzebowałem, aby znaleźć, zrozumieć i naprawić regresje, które miały miejsce. Dane to moc. –

+0

ah fair, po prostu różne style naprawdę. Zestaw testowy musi być używany przez kilka osób, więc raczej uczyni to oczywistym dla programisty, który pracuje nad nim z góry, niż musi przechodzić przez pliki i sprawdzać. – Barry