2 votes

Comparer l'égalité de deux objets sur la base de dictionnaires

J'ai deux objets avec ces définitions :

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

Le schéma de la classe Container est le suivant :

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; }
}

Ma question est la suivante : comment puis-je vérification approfondie l'équité de cont1 et cont2 ? Je veux dire l'égalité de chaque membre et de chaque valeur, même au plus profond des objets de Subs ;

Existe-t-il une fonctionnalité en c# pour de telles situations ou dois-je écrire moi-même une méthode personnalisée pour vérifier l'égalité sur la base de la structure des objets ?

Deuxième question : Je peux éviter le problème d'égalité si je peux créer deux copies différentes de Products ; je veux dire que nous avons un objet Container de base avec tous les membres et toutes les valeurs et que nous créons ensuite deux exemplaires séparés de conteneurs, à savoir cont1 et cont2, pour lesquels le changement d'une valeur dans cont1 ne changera pas la même valeur dans cont2.

Note1 Cette méthode de clonage ne fonctionne pas :

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

Note2 La plupart des méthodes proposées dans les autres réponses sont basées sur un dictionnaire à un seul niveau (en utilisant des boucles ou LINQ pour la vérification), ce qui n'est pas le cas lorsque nous avons des propriétés et des objets de dictionnaire (qui ont leurs propres propriétés) dans l'objet.

2voto

Jamiec Points 35773

Un dictionnaire est une séquence, donc en général ce que vous recherchez est Enumerable<T>.SequenceEquals qui permet de passer un IEquityComparer<T> .

Votre séquence (Dictionnaire) est un IEnumerable<KeyValuePair<string,Container>> Vous avez donc besoin d'un comparateur qui implémente IEquityComparer<IEnumerable<KeyValuePair<string,Container>>> (Cela fait beaucoup d'équerres !).

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

Notez que l'ordre des dictionnaires d'éléments n'est pas garanti, de sorte que pour utiliser la méthode correctement, vous devriez probablement utiliser OrderBy avant de comparer les séquences, mais cela ajoute à l'inefficacité de cette méthode.


Pour votre deuxième question, vous essayez de faire ce qui suit Clone le dictionnaire. En général, votre Container devrait mettre en œuvre ICloneable que vous pouvez ensuite utiliser pour créer une copie

var cont2 = cont1.ToDictionary(k => k.Key, v => v.Value.Clone());

1voto

Tim Schmelter Points 163781

Oui, vous devez écrire vous-même une méthode personnalisée pour vérifier l'égalité sur la base de la structure des objets. Je fournirais une méthode IEqualityComparer<Container> et un IEqualityComparer<Sub> comme ici ( GetHashCode sur la base d'une mise en œuvre cette ):

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;
        }
    }
} 

Cette opération devrait permettre de vérifier en profondeur toutes les propriétés et les dictionnaires. Vous pouvez ensuite utiliser la boucle suivante pour comparer les deux dictionnaires l'un à l'autre :

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
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X