Nous avons déjà analysé les particularités des structures du framework .NET qui représentent les types de valeur lors de la comparaison d'objets par valeur - instance de structures.
Maintenant, je vais décrire ce processus sur un exemple particulier pour vérifier s'il nous permettra de déterminer l'utilisation de la comparaison d'objets par valeur en général et ainsi, de simplifier un échantillon de comparaison d'objets par valeur - des instances de classe qui représentent la référence type.
La structure PersonStruct :
using System; namespace HelloEquatable { public struct PersonStruct : IEquatable<PersonStruct>, IEquatable<PersonStruct?> { private static int GetHashCodeHelper(int[] subCodes) { int result = subCodes[0]; for (int i = 1; i < subCodes.Length; i++) result = unchecked(result * 397) ^ subCodes[i]; return result; } private static string NormalizeName(string name) => name?.Trim() ?? string.Empty; private static DateTime? NormalizeDate(DateTime? date) => date?.Date; public string FirstName { get; } public string LastName { get; } public DateTime? BirthDate { get; } public PersonStruct(string firstName, string lastName, DateTime? birthDate) { this.FirstName = NormalizeName(firstName); this.LastName = NormalizeName(lastName); this.BirthDate = NormalizeDate(birthDate); } public override int GetHashCode() => GetHashCodeHelper( new int[] { this.FirstName.GetHashCode(), this.LastName.GetHashCode(), this.BirthDate.GetHashCode() } ); public static bool Equals(PersonStruct first, PersonStruct second) => first.BirthDate == second.BirthDate && first.FirstName == second.FirstName && first.LastName == second.LastName; public static bool operator ==(PersonStruct first, PersonStruct second) => Equals(first, second); public static bool operator !=(PersonStruct first, PersonStruct second) => !Equals(first, second); public bool Equals(PersonStruct other) => Equals(this, other); public static bool Equals(PersonStruct? first, PersonStruct? second) => first == second; // Alternate version: //public static bool Equals(PersonStruct? first, PersonStruct? second) => // first.HasValue == second.HasValue && // ( // !first.HasValue || Equals(first.Value, second.Value) // ); public bool Equals(PersonStruct? other) => this == other; // Alternate version: //public bool Equals(PersonStruct? other) => // other.HasValue && Equals(this, other.Value); public override bool Equals(object obj) => (obj is PersonStruct) && Equals(this, (PersonStruct)obj); // Alternate version: //public override bool Equals(object obj) => // obj != null && // this.GetType() == obj.GetType() && // Equals(this, (PersonStruct)obj); } }
Comme vous pouvez le voir, cet exemple est plus petit et plus facile par structure, car les instances de structures ne sont pas nulles et il n'est pas possible d'hériter de structures définies par l'utilisateur. Nous avons déjà abordé les particularités pour implémenter la comparaison par valeur pour les instances de classe dans mon précédent article.
De plus, nous avons déterminé des champs pour la comparaison d'objets et implémenté la méthode GetHashCode().
Les méthodes et opérateurs de comparaison ont été implémentés dans l'ordre suivant :
- Pour comparer deux instances de structures, nous avons implémenté la méthode statique PersonStruct.Equals(PersonStruct, PersonStruct). Nous utiliserons cette méthode comme méthode de comparaison de référence lors de la mise en œuvre d'autres méthodes et opérateurs. De plus, il peut être appliqué pour comparer des instances de structures dans des langages qui ne prennent pas en charge les opérateurs.
- Les opérateurs PersonStruct.==(PersonStruct, PersonStruct) et PersonStruct.!=(PersonStruct, PersonStruct) ont également été implémentés. A noter qu'un compilateur C# a les particularités suivantes :
- Vous pouvez comparer avec les opérateurs surchargés T.==(T, T) et T.!=(T, T) dans Nullable(Of T)
- Avant de vérifier l'égalité d'une valeur, un compilateur peut vérifier si les instances de structs ont une valeur valide. De plus, un compilateur n'encapsule pas les instances de structures dans des objets.
- Ainsi, comparer des instances de la structure Nullable(Of T) avec une valeur nullable non typée conduit à appeler les opérateurs ==(T, T) ou T.!=(T, T), tout en comparant des instances de la structure Nullable( De la structure T) sans opérateurs surchargés T.==(T, T) et T.!=(T, T) entraîne l'appel des opérateurs Object.==(Object, Object) ou Object.!=(Object, Object) et, par conséquent, en enveloppant une instance dans l'objet.
- La méthode PersonStruct.Equals(PersonStruct) (implémentation de IEquatable(Of PersonStruct)) a été implémentée en appelant la méthode PersonStruct.Equals(PersonStruct, PersonStruct).
- Pour éviter d'encapsuler des instances de structs dans des objets, lorsque nous avons une ou deux instances Nullable(Of PersonStruct), il est possible d'implémenter les méthodes suivantes :
- PersonStruct.Equals(PersonStruct ?, PersonStruct ?), en tant qu'appel de l'opérateur PersonStruct.==(PersonStruct, PersonStruct), est utilisé pour éviter d'encapsuler des instances de structs des deux arguments dans des objets et d'appeler Object.Equals( Object, Object) si au moins un des arguments est une instance Nullable(Of PersonStruct). De plus, vous pouvez utiliser cette méthode pour comparer des instances Nullable(Of PersonStruct) dans des langages qui ne prennent pas en charge les opérateurs. Dans le code, vous pouvez trouver des commentaires expliquant comment cette méthode pourrait être implémentée si un compilateur C# n'était pas en mesure d'utiliser les opérateurs T.==(T, T) et T.!=(T, T) pour les opérateurs Nullable(Of T) arguments.
- PersonStruct.Equals(PersonStruct ?) :implémentation de l'interface IEquatable(Of PersonStruct?) utilisée pour éviter d'encapsuler les arguments Nullable(Of PersonStruct) dans des objets et d'appeler la méthode PersonStruct.Equals(Object). Il est implémenté comme un appel de l'opérateur PersonStruct.==(PersonStruct, PersonStruct) avec le code commenté pour utiliser les opérateurs T.==(T, T) et T.!=(T, T) pour les opérateurs Nullable(Of T ) arguments.
- PersonStruct.Equals(Object) – qui remplace la méthode Object.Equals(Object). Il est implémenté en vérifiant la compatibilité d'un type d'argument avec un type de l'objet actuel à l'aide de l'opérateur is en convertissant l'argument en PersonStruct et en appelant PersonStruct.Equals(PersonStruct, PersonStruct).
Remarques :
- L'implémentation de l'interface IEquatable(Of PersonStruct ?) — IEquatable(Of Nullable(Of PersonStruct)) sert à montrer des problèmes particuliers dans la plate-forme lorsque vous travaillez avec des structures où l'encapsulation d'instances dans des objets se produit plus rapidement que prévu.
- Dans les projets réels, à condition qu'il ne soit pas nécessaire d'améliorer les performances, l'implémentation de IEquatable(Of Nullable(Of T)) n'est pas applicable pour des raisons d'architecture - nous ne devrions pas implémenter IEquatable typé dans le type T pour n'importe quel type.
- En général, il n'est pas nécessaire de surcharger un code avec différentes optimisations.
Pour les structures, nous pourrions parvenir à rendre la comparaison par valeur beaucoup plus simple et plus productive en évitant l'héritage des structures définies par l'utilisateur et la nécessité de vérifier les objets sur null. De plus, nous pouvons surveiller une nouvelle logique qui prend en charge les arguments Nullable(Of T).
Dans ma future publication, je résumerai les points suivants :
- Lorsque c'est une bonne idée d'implémenter la comparaison d'objets par valeur ;
- Comment pouvons-nous simplifier la mise en œuvre de la comparaison par valeur pour les objets – instances de classe qui représentent des types de référence.
Lire aussi :
Comparaison d'objets par valeur. Partie 1 :Début
Comparaison d'objets par valeur. Partie 2 : Notes de mise en œuvre de la méthode Equals
Comparaison d'objets par valeur. Partie 3 :Opérateurs d'égalité et d'égalité spécifiques au type
Comparaison d'objets par valeur. Partie 4 :Opérateurs d'héritage et de comparaison
Comparaison d'objets par valeur. Partie 5 :Problème d'égalité des structures