J'ai deux objets complexes comme Object1
et Object2
. Ils ont environ 5 niveaux d'objets enfants.
J'ai besoin de la méthode la plus rapide pour dire s'ils sont identiques ou non.
Comment cela pourrait-il être fait en C # 4.0?
Implémentez IEquatable<T>
(généralement en même temps que le remplacement des méthodes Object.Equals
et héritées Object.GetHashCode
) sur tous vos types personnalisés. Dans le cas des types composites, invoquez la Equals
méthode des types contenus dans les types conteneurs . Pour les collections contenues, utilisez la SequenceEqual
méthode d'extension, qui appelle en interne IEquatable<T>.Equals
ou Object.Equals
sur chaque élément. Cette approche vous obligera évidemment à étendre les définitions de vos types, mais ses résultats sont plus rapides que toutes les solutions génériques impliquant la sérialisation.
Edit : Voici un exemple artificiel avec trois niveaux d'imbrication.
Pour les types valeur, vous pouvez généralement simplement appeler leur Equals
méthode. Même si les champs ou les propriétés n'étaient jamais explicitement affectés, ils auraient toujours une valeur par défaut.
Pour les types de référence, vous devez d'abord appeler ReferenceEquals
, qui vérifie l'égalité des références - cela servirait d'amélioration de l'efficacité lorsque vous référencez le même objet. Il traiterait également les cas où les deux références sont nulles. Si cette vérification échoue, confirmez que le champ ou la propriété de votre instance n'est pas nul (à éviter NullReferenceException
) et appelez sa Equals
méthode. Puisque nos membres sont correctement typés, la IEquatable<T>.Equals
méthode est appelée directement, en contournant la Object.Equals
méthode surchargée (dont l'exécution serait légèrement plus lente en raison du type cast).
Lorsque vous remplacez Object.Equals
, vous êtes également censé remplacer Object.GetHashCode
; Je ne l'ai pas fait ci-dessous par souci de concision.
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
Mise à jour : Cette réponse a été écrite il y a plusieurs années. Depuis lors, j'ai commencé à m'éloigner de l'implémentation IEquality<T>
de types mutables pour de tels scénarios. Il existe deux notions d'égalité: l' identité et l' équivalence . Au niveau de la représentation de la mémoire, ils sont généralement distingués comme «égalité de référence» et «égalité de valeur» (voir Comparaisons d'égalité ). Cependant, la même distinction peut également s'appliquer au niveau du domaine. Supposons que votre Person
classe possède une PersonId
propriété, unique par personne distincte du monde réel. Est-ce que deux objets avec les mêmes valeurs PersonId
mais différentes Age
doivent être considérés comme égaux ou différents? La réponse ci-dessus suppose que l'on est après l'équivalence. Cependant, il existe de nombreux usages duIEquality<T>
interface, comme les collections, qui supposent que de telles implémentations fournissent une identité . Par exemple, si vous remplissez a HashSet<T>
, vous vous attendez généralement à ce qu'un TryGetValue(T,T)
appel renvoie des éléments existants qui partagent simplement l'identité de votre argument, pas nécessairement des éléments équivalents dont le contenu est complètement le même. Cette notion est renforcée par les notes sur GetHashCode
:
En général, pour les types de référence mutables, vous ne devez remplacer
GetHashCode()
que si:
- Vous pouvez calculer le code de hachage à partir de champs qui ne sont pas modifiables; ou
- Vous pouvez vous assurer que le code de hachage d'un objet mutable ne change pas tant que l'objet est contenu dans une collection qui repose sur son code de hachage.
partial
- auquel cas, oui, vous pouvez implémenter leurEquals
méthode via une déclaration de classe partielle ajoutée manuellement qui fait référence aux champs / propriétés de la génération automatique une.Enumerable.SequenceEqual
méthode sur les tableaux:this.Addresses.SequenceEqual(other.Addresses)
. Cela appellerait en interne votreAddress.Equals
méthode pour chaque paire d'adresses correspondantes, étant donné que laAddress
classe implémente l'IEquatable<Address>
interface.Sérialiser les deux objets et comparer les chaînes résultantes
la source
+1
simplement parce que je n'ai jamais pensé à faire une comparaison d'égalité basée sur des valeurs de cette façon. C'est sympa et simple. Ce serait bien de voir quelques points de repère avec ce code.Vous pouvez utiliser la méthode d'extension, la récursivité pour résoudre ce problème:
ou comparer en utilisant Json (si l'objet est très complexe) Vous pouvez utiliser Newtonsoft.Json:
la source
DeepCompare
au lieu d'appeler simplementCompareEx
récursivement?result
parreturn false
le rendrait plus efficace.Si vous ne souhaitez pas implémenter IEquatable, vous pouvez toujours utiliser Reflection pour comparer toutes les propriétés: - si elles sont de type valeur, comparez-les simplement - si elles sont de type référence, appelez la fonction de manière récursive pour comparer ses propriétés "internes" .
Je ne pense pas à la performance, mais à la simplicité. Cela dépend cependant de la conception exacte de vos objets. Cela peut devenir compliqué en fonction de la forme de vos objets (par exemple s'il existe des dépendances cycliques entre les propriétés). Il existe cependant plusieurs solutions que vous pouvez utiliser, comme celle-ci:
Une autre option consiste à sérialiser l'objet sous forme de texte, par exemple à l'aide de JSON.NET, et à comparer le résultat de la sérialisation. (JSON.NET peut gérer les dépendances cycliques entre les propriétés).
Je ne sais pas si par plus rapide vous entendez le moyen le plus rapide de l'implémenter ou un code qui s'exécute rapidement. Vous ne devez pas optimiser avant de savoir si vous en avez besoin. L'optimisation prématurée est la racine de tout Mal
la source
IEquatable<T>
implémentation puisse être qualifiée de cas d'optimisation prématurée. La réflexion sera considérablement plus lente. L'implémentation par défaut deEquals
pour les types de valeur personnalisés utilise la réflexion; Microsoft lui-même recommande de le remplacer pour les performances: «Remplacez laEquals
méthode pour un type particulier pour améliorer les performances de la méthode et représenter plus fidèlement le concept d'égalité pour le type.»Sérialisez les deux objets et comparez les chaînes résultantes par @JoelFan
Donc, pour ce faire, créez une classe statique comme celle-ci et utilisez les extensions pour étendre TOUS les objets (afin que vous puissiez passer n'importe quel type d'objet, de collection, etc. dans la méthode)
Une fois que vous avez référencé cette classe statique dans un autre fichier, vous pouvez le faire:
Vous pouvez maintenant simplement utiliser .Equals pour les comparer. J'utilise ceci pour vérifier si les objets sont également dans des collections. Cela fonctionne vraiment bien.
la source
CultrureInfo
. Cela ne fonctionnerait que si les données internes sont principalement des chaînes et des entiers. Sinon, ce serait un désastre.Je suppose que vous ne faites pas référence aux mêmes objets
Vous pensez peut-être faire une comparaison de mémoire entre les deux
Mais ce n'est même pas du vrai code en c # :). Étant donné que toutes vos données sont probablement créées sur le tas, la mémoire n'est pas contiguë et vous ne pouvez pas simplement comparer l'égalité de deux objets de manière agnostique. Vous allez devoir comparer chaque valeur, une à la fois, de manière personnalisée.
Envisagez d'ajouter l'
IEquatable<T>
interface à votre classe et définissez uneEquals
méthode personnalisée pour votre type. Ensuite, dans cette méthode, testez manuellement chaque valeur. Ajoutez àIEquatable<T>
nouveau les types inclus si vous le pouvez et répétez le processus.la source
Sérialisez les deux objets, puis calculez le code de hachage, puis comparez.
la source
J'ai trouvé cette fonction ci-dessous pour comparer des objets.
Je l'utilise et cela fonctionne très bien pour moi.
la source
if
signifie ceCompare(null, null) == false
qui n'est pas ce à quoi je m'attendais.Sur la base de quelques réponses déjà données ici, j'ai décidé de soutenir principalement la réponse de JoelFan . J'adore les méthodes d'extension et celles-ci fonctionnent très bien pour moi alors qu'aucune des autres solutions ne les utiliserait pour comparer mes classes complexes.
Méthodes d'extension
Exemples d'utilisation
la source
Je dirais que:
Object1.Equals(Object2)
serait ce que vous recherchez. C'est si vous cherchez à voir si les objets sont les mêmes, ce que vous semblez demander.
Si vous souhaitez vérifier si tous les objets enfants sont identiques, exécutez-les via une boucle avec la
Equals()
méthode.la source
la source
Une façon de le faire serait de remplacer
Equals()
chaque type impliqué. Par exemple, votre objet de niveau supérieur remplaceraitEquals()
pour appeler laEquals()
méthode des 5 objets enfants. Ces objets doivent également tous être remplacésEquals()
, en supposant qu'il s'agit d'objets personnalisés, et ainsi de suite jusqu'à ce que toute la hiérarchie puisse être comparée en effectuant simplement une vérification d'égalité sur les objets de niveau supérieur.la source
Utilisez l'
IEquatable<T>
interface qui a une méthodeEquals
.la source
Merci à l'exemple de Jonathan. Je l'ai développé pour tous les cas (tableaux, listes, dictionnaires, types primitifs).
Il s'agit d'une comparaison sans sérialisation et ne nécessite l'implémentation d'aucune interface pour les objets comparés.
Pour une copie facile du référentiel de code créé
la source
Vous pouvez maintenant utiliser json.net. Allez simplement sur Nuget et installez-le.
Et vous pouvez faire quelque chose comme ceci:
Vous pourriez peut-être créer une méthode d'extension pour object si vous voulez devenir plus sophistiqué. Veuillez noter que cela ne compare que les propriétés publiques. Et si vous souhaitez ignorer une propriété publique lorsque vous effectuez la comparaison, vous pouvez utiliser l'attribut [JsonIgnore].
la source