2012-12-19 7 views
8

Powiedzmy mam taką strukturęWzór dostać obiektów o strukturze różnicę i statystyki czyli jak stworzyć diff narzędzie

public class Form 
{ 
    #region Public Properties 

    public List<Field> Fields { get; set; } 

    public string Id { get; set; } 

    public string Name { get; set; } 

    public string Version { get; set; } 

    public int Revision { get; set; } 

    #endregion 
} 

Więc klasa Form zawiera listę pól, powiedzmy, że samo pole jest reprezentowane przez takie struktura

public class Field 
{ 
    #region Public Properties 

    public string DisplayName { get; set; } 

    public List<Field> Fields { get; set; } 

    public string Id { get; set; } 

    public FieldKind Type { get; set; } 

    public FieldType FieldType { get; set; } 

    #endregion 
} 

The Field jest zasadniczo struktura kompozytowa, każdy Field zawiera listę pól podrzędnych. Struktura jest więc hierarchiczna. Również Field ma odniesienie do klasy FieldType.

public class FieldType 
{ 
    #region Public Properties 

    public DataType DataType { get; set; } 

    public string DisplayName { get; set; } 

    public string Id { get; set; } 

    #endregion 
} 

I na koniec mamy odniesienie do DataType

public class DataType 
{ 
    #region Public Properties 

    public string BaseType { get; set; } 

    public string Id { get; set; } 

    public string Name { get; set; } 

    public List<Restriction> Restrictions { get; set; } 

    #endregion 
} 

Co chcę osiągnąć jest uzyskanie różnicy takiej skomplikowanej strukturze, powiedzmy, że jeśli mam jakąś porównywarka Będzie daj mi strukturalną różnicę dla całej formy jako jednej klasy, powiedzmy RóżnicaRezult. Kiedy mówiłem o strukturalnej różnicy, mam na myśli, że powinno to być coś takiego.

  1. Różnica dla pól Form (wyjście Form ma różnicy w polu Wersja (inny kolor)
  2. Różnice w zakresie zbierania Fields, w tym hierarchii pola do pola pod redakcją
  3. samo zachowanie dla FieldType i DataType
  4. Wykrywanie usuwanie i dodawanie Field do Form (więc zapewne każdy Różnica będzie miał typ)

Co mam teraz. Zacząłem ogólne podejście i próbowali wykorzystać ReflectionComparer realizacji sprzęgowi IEqualityComparer

public bool Equals(T x, T y) 
{ 
    var type = typeof(T); 

    if (typeof(IEquatable<T>).IsAssignableFrom(type)) 
    { 
     return EqualityComparer<T>.Default.Equals(x, y); 
    } 

    var enumerableType = type.GetInterface(typeof(IEnumerable<>).FullName); 

    if (enumerableType != null) 
    { 
     var elementType = enumerableType.GetGenericArguments()[0]; 
     var elementComparerType = typeof(DifferenceComparer<>).MakeGenericType(elementType); 

     var elementComparer = Activator.CreateInstance(elementComparerType, new object[] { _foundDifferenceCallback, _existedDifference }); 

     return (bool)typeof(Enumerable).GetGenericMethod("SequenceEqual", new[] { typeof(IEnumerable<>), typeof(IEnumerable<>), typeof(IEqualityComparer<>) }).MakeGenericMethod(elementType).Invoke(null, new[] { x, y, elementComparer }); 
    } 

    foreach (var propertyInfo in type.GetProperties()) 
    { 
     var leftValue = propertyInfo.GetValue(x); 
     var rightValue = propertyInfo.GetValue(y); 

     if (leftValue != null) 
     { 
      var propertyComparerType = typeof(DifferenceComparer<>).MakeGenericType(propertyInfo.PropertyType); 

      var propertyComparer = Activator.CreateInstance(propertyComparerType, new object[] {_foundDifferenceCallback, _existedDifference}); 

      if (!((bool)typeof(IEqualityComparer<>).MakeGenericType(propertyInfo.PropertyType) 
       .GetMethod("Equals") 
       .Invoke(propertyComparer, new object[] { leftValue, rightValue }))) 
      { 
       // Create and publish the difference with its Type 
      } 
     } 
     else 
     { 
      if (!Equals(leftValue, rightValue)) 
      { 
       // Create and publish the difference with its Type 
      } 
     } 
    } 

    //return true if no differences are found 
} 

I Difference klasę

public struct Difference 
{ 
    public readonly string Property; 

    public readonly DifferenceType Type; 

    public readonly IExtractionable Expected; 

    public readonly IExtractionable Actual; 
} 

Ale prawdopodobnie nie jest to sposób, w jaki chcę iść, bo mam porównanie Pole ściślej biorąc w biorąc pod uwagę, że każde pole ma identyfikator, który może być różny również dla różnych formularzy i chcę przejąć większą kontrolę nad procesem porównywania. Dla mnie brzmi to bardziej jak narzędzie do porównywania.

Poszukuję więc dobrego wzoru i całkiem dobrej struktury, aby opublikować różnicę w kodzie klienta, która może z łatwością ją zwizualizować?

+1

Dlaczego zdecydowałeś się do nas e odbicie na pierwszym miejscu? Twoje zajęcia są znane i mocno napisane. Czy planujesz użyć tego również na innych zajęciach? – Groo

+0

Może to pytanie SO jest powiązane z twoim: [link do pytania SO] (http://stackoverflow.com/questions/2387946/finding-property-differences-between-two-c-sharp-objects) i nawet jeśli podane Odpowiedź jest przydatna, bardzo polecam zajrzeć do drugiej odpowiedzi, która wskazuje na [testapi.codeplex.com] (http://testapi.codeplex.com/), która ma "API głębokiego porównywania obiektów" opisaną w części 8. – jCoder

+0

dobrze, niezupełnie, nie ma innych zajęć w tej chwili. pomysł polegał na stworzeniu elastycznego rozwiązania, które następnie może być wykorzystane w przyszłości do porównania również innych klas, więc najpierw spróbowałem refleksji. –

Odpowiedz

1

Po zbadaniu bitu i porównaniu różnych zasobów zaimplementowałem taki algorytm.

  1. Weź swój obiekt i skonstruować wykres (z użyciem .GetType(). GetProperties(), dodając każdą nieruchomość jako węzła potomnego węzła głównego Ciebie. Kluczem węzła będzie nazwa właściwości.
  2. Idź rekurencyjnie jeśli spełniają właściwości każdego typu niestandardowego lub IEnumerable.
  3. Tworzenie wykresu porównywarka i proste porównanie dwóch wykresów

Później będę publikować moje kod i zaktualizuj odpowiedź.

+0

Chciałbym zobaczyć ten kod – Muaddib

1

Biorąc pod uwagę twoje zajęcia są bardzo dobrze już znany, będę korzystać z następujących rzeczy:

  • interfejs IDiffable, który sprawia, że ​​mają klasę (inna T) Klasa DiffWith, wracając wyliczenie różnic . To ma sens, aby zaimplementować twoją DiffWith w każdej z twoich klas, co powinno być banalne, biorąc pod uwagę, że są one w dużym stopniu poprawione.

  • Oddzielna klasa ReflectionDiff (prawdopodobnie statyczna), która porównałaby sposób, w jaki robisz, za pomocą odbicia.Ten może używać atrybutów do umieszczania na białej liście/ignorowania dowolnej właściwości/pola. W ten sposób możesz mieć domyślny silnik diff do szybkiej implementacji różnic i używać dokładnie tego rodzaju kodu odbicia, którego używasz. Można po prostu implementację diff z niczym:

    public override IEnumerable<Difference> DiffWith(Field other) 
    { 
        return ReflectionDiff.Diff(this, other); 
    } 
    

dostać jeden jedną klasę z całą brudną kodu refleksji (która tak naprawdę nie jest to konieczne) i może mieć prosty w realizacji DiffWith każda z twoich trudnych klas.

0

Nie wiem, jak elegancki rozwiązanie jesteś po, ale jeśli tylko chcieliśmy dość szybkiego rozwiązania z minimalnym kodowania to może:

  1. serializacji instancji być porównane do formatu tekstowego (np jak JSON przy użyciu Json.NET)
  2. napisać odcinkach obiekty do plików tekstowych
  3. uruchomienie narzędzia diff przekazując pliki tekstowe 2 jako argumenty (coś jak ExamDiff zrobi to łatwo)

Tak jak powiedziałem, jest to szybkie rozwiązanie, ale może być wystarczająco dobre dla twoich potrzeb. W przeciwnym razie dostępne są narzędzia różnicowe wiersza polecenia, które będą wyświetlać plik diff w stylu unixa, za który można następnie napisać własny wizualizator.

Powiązane problemy