2016-05-12 14 views
2

Ich habe zwei Objekte mit diesen Definitionen:Vergleichen Gleichheit zweier Objekte basierend auf Wörterbücher

public static Dictionary<string, Container> cont1 = new Dictionary<string, Container>(); 
public static Dictionary<string, Container> cont2 = new Dictionary<string, Container>(); 

Das Schema der Container-Klasse ist wie folgt:

public class Container 
{ 
    public string IDx { get; set; } 
    public string IDy { get; set; } 
    public string Name { get; set; } 
    public Dictionary<string, Sub> Subs = new Dictionary<string, Sub>(); 
} 

public class Sub 
{ 
    public string Namex { get; set; } 
    public string Namey { get; set; } 
    public string Value { get; set; } 
    public Dictionary<string, string> Paths { get; set; } 
} 

Meine Frage ist: Wie kann ich tiefe Prüfung das Eigenkapital von cont1 und cont2? Ich meine die Gleichheit aller Mitglieder und Werte, sogar tief in Sub-Objekten;

Gibt es irgendeine Funktionalität in C# für solche Situationen oder ich muss eine benutzerdefinierte Methode für die Überprüfung der Gleichheit basierend auf der Struktur der Objekte selbst schreiben;

Zweite Frage: Ich kann das Gleichheitsproblem umgehen, wenn ich zwei verschiedene Kopien von Produkten erstellen kann; Ich meine, sagen wir, wir haben ein Basis-Container-Objekt mit allen Mitgliedern und Werten und dann erstellen zwei separate Kopien Container, nämlich cont1 und cont2, die Änderung eines Wertes in cont1 wird nicht den gleichen Wert in cont2 ändern.

Note1: Diese Methode für das Klonen nicht funktioniert:

cont2 = new Dictionary<string, Container>(cont1); 

Note2: Die meisten der vorgeschlagenen Methoden in anderen Antworten sind auf einer einer Ebene Wörterbuch basiert (mit Schleifen oder LINQ für die Überprüfung) und nicht so ein Fall, wenn wir Eigenschaften und Wörterbuchobjekte (die ihre eigenen Eigenschaften haben) innerhalb des Objekts haben.

+0

'Meine Frage ist: Wie kann ich tief das Eigenkapital der cont1 und cont2' überprüfen - bedeutet, dass beide Wörterbücher die gleichen Schlüssel haben. und der Wert jedes Schlüssels hat die gleichen exakten * Werte * oder die gleichen exakten * Instanzen * von 'Container'? – Jamiec

+0

Natürlich müssen Sie eine benutzerdefinierte Methode zum Überprüfen der Gleichheit basierend auf der Struktur der Objekte selbst schreiben. Aber es ist nicht einmal klar, was Sie als gleich verstehen. Stellen Sie auch nicht mehrere Fragen auf einmal. –

+0

Sie können verschiedene Schlüssel und Werte haben und das ist der Punkt; um zu überprüfen, ob sie genau die gleichen Schlüssel haben, Eigenschaften mit den gleichen Werten – wiki

Antwort

1

Ja, Sie haben eine eigene Methode schreiben Gleichheit für die Überprüfung auf der Grundlage der Struktur der Objekte selbst. Ich würde eine benutzerdefinierte IEqualityComparer<Container> und eine wie hier (GetHashCode Implementierung basiert auf this):

public class ContainerCheck : IEqualityComparer<Container> 
{ 
    private SubCheck subChecker = new SubCheck(); 
    public bool Equals(Container x, Container y) 
    { 
     if (ReferenceEquals(x, y)) 
      return true; 
     if (x == null || y == null) 
      return false; 
     if (x.IDx != y.IDx || x.IDy != y.IDy || x.Name != y.Name) 
      return false; 
     // check dictionary 
     if (ReferenceEquals(x.Subs, y.Subs)) 
      return true; 
     if (x.Subs == null || y.Subs == null || x.Subs.Count != y.Subs.Count) 
      return false; 
     foreach (var kv in x.Subs) 
      if (!y.Subs.ContainsKey(kv.Key) || subChecker.Equals(y.Subs[kv.Key], kv.Value)) 
       return false; 
     return true; 

    } 

    public int GetHashCode(Container obj) 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + obj.IDx.GetHashCode(); 
      hash = hash * 23 + obj.IDy.GetHashCode(); 
      hash = hash * 23 + obj.Name.GetHashCode(); 
      foreach (var kv in obj.Subs) 
      { 
       hash = hash * 23 + kv.Key.GetHashCode(); 
       hash = hash * 23 + subChecker.GetHashCode(kv.Value); 
      } 

      return hash; 
     } 
    } 
} 

public class SubCheck : IEqualityComparer<Sub> 
{ 
    public bool Equals(Sub x, Sub y) 
    { 
     if (ReferenceEquals(x, y)) 
      return true; 
     if (x == null || y == null) 
      return false; 
     if (x.Namex != y.Namex || x.Namey != y.Namey || x.Value != y.Value) 
      return false; 
     // check dictionary 
     if (ReferenceEquals(x.Paths, y.Paths)) 
      return true; 
     if (x.Paths == null || y.Paths == null || x.Paths.Count != y.Paths.Count) 
      return false; 
     foreach(var kv in x.Paths) 
      if (!y.Paths.ContainsKey(kv.Key) || y.Paths[kv.Key] != kv.Value) 
       return false; 
     return true; 
    } 

    public int GetHashCode(Sub obj) 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + obj.Namex.GetHashCode(); 
      hash = hash * 23 + obj.Namey.GetHashCode(); 
      hash = hash * 23 + obj.Value.GetHashCode(); 
      foreach (var kv in obj.Paths) 
      { 
       hash = hash * 23 + kv.Key.GetHashCode(); 
       hash = hash*23 + kv.Value.GetHashCode(); 
      } 

      return hash; 
     } 
    } 
} 

Dies sollte tief alle Eigenschaften überprüfen und die Wörterbücher.Dann könnten Sie folgende Schleife verwenden, um beide Wörterbücher miteinander zu vergleichen:

bool equal = true; 
var allKeys = cont1.Keys.Concat(cont2.Keys).ToList(); 
var containerChecker = new ContainerCheck(); 

foreach (string key in allKeys) 
{ 
    Container c1; 
    Container c2; 
    if (!cont1.TryGetValue(key, out c1) || !cont2.TryGetValue(key, out c2)) 
    { 
     equal = false; 
    } 
    else 
    { 
     // deep check both containers 
     if (!containerChecker.Equals(c1, c2)) 
      equal = false; 
    } 
    if(!equal) 
     break; // or collect differences 
} 
+0

Vielen Dank für den Nachweis der vollständigen Implementierung! Wie auch immer, ich war neugierig, ob es eine Verknüpfung für solche Situationen gab oder nicht – wiki

+0

@wiki: nein, leider nicht. Sie haben immer 'Object.ReferenceEquals', wie es auch in meiner Implementierung verwendet wird. Aber wenn Sie 'Equals' +' GetHashCode'yourself nicht überschreiben (oder ein 'IEqualityComparer ' bereitstellen), wird alles, was .NET verwenden wird 'ReferenceEquals'. Es wird also nur überprüft, ob beide die gleichen Referenzen sind und nicht, wenn die Eigenschaften gleich sind. –

2

Ein Dictionary ist eine Sequenz, also im Allgemeinen, was Sie wahrscheinlich suchen, ist Enumerable<T>.SequenceEquals, die in einem IEquityComparer<T> übergeben ermöglicht.

Ihre Sequenz (Dictionary) ist ein IEnumerable<KeyValuePair<string,Container>>, also benötigen Sie einen Vergleich, der implementiert (das ist eine Menge Winkelstreben!).

var equal = cont1.SequenceEquals(cont2, new StringContainerPairEquityComparer()); 

Beachten Sie, dass die Reihenfolge der Elemente Wörterbücher sind nicht garantiert, so die Methode richtig zu nutzen, sollten Sie wahrscheinlich OrderBy verwenden, bevor Sequenzen zu vergleichen - aber fügt dies die Ineffizienz dieser Methode.


Für Ihre zweite Frage, was Sie versuchen, das Wörterbuch zu tun ist, Clone. Im Allgemeinen sollten Sie Ihre ContainerICloneable Schnittstelle implementieren, die Sie dann

eine Kopie erstellen können
var cont2 = cont1.ToDictionary(k => k.Key, v => v.Value.Clone()); 
+0

Ja; Du hast recht; und deshalb stelle ich diese Frage, um zu wissen, ob es einen einfacheren Weg gibt. – wiki

+0

@wiki - einfacher als ..... was? 1 Klasse implementieren? – Jamiec

+0

einfacher als eine lange Methode basierend auf der Struktur meiner Klassen zu implementieren, um auf Gleichheit zu überprüfen – wiki