📜 ⬆️ ⬇️

Comparing objects by value - 6: Structure Equality Implementation

In the previous publication, we reviewed the features of the device and operation of the structures of the .NET platform, which are "types by value" (Value Types) in the context of comparison by the value of objects - instances of structures .


Now we will consider a ready-made example of the implementation of comparison by the value of objects - instances of structures .


Will the example for structures help to more accurately determine from the subject (domain) point of view the scope of comparison of objects by value as a whole, and thereby simplify the comparison pattern by value of objects - instances of classes that are Reference Types , derived in one of the previous publications ?


PersonStruct structure:


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

An example with the implementation of comparing objects by value for structures smaller in size and simpler in structure due to the fact that instances of structures cannot accept null values ​​and the fact that it is impossible to inherit from user-defined structures (User defined structs) the value of objects - instances of classes, taking into account inheritance, are considered in the fourth publication of this cycle).


Similar to the previous examples, the fields for comparison are defined and the GetHashCode () method is implemented.


Methods and comparison operators are implemented sequentially as follows:


  1. The static method PersonStruct.Equals (PersonStruct, PersonStruct) is implemented to compare two instances of structures.
    This method will be used as a reference comparison method when implementing other methods and operators.
    This method can also be used to compare instances of structures in languages ​​that do not support operators.


  2. The PersonStruct. == (PersonStruct, PersonStruct) and PersonStruct.! = (PersonStruct, PersonStruct) operators are implemented.
    It should be noted that the C # compiler has an interesting feature:


    • If the structure T has overloaded operators T. == (T, T) and T.! = (T, T), for the structures Nullable (Of T) it also becomes possible to compare using the operators T. == (T, T) and T.! = (T, T).
    • This is probably the compiler's "magic", which checks for the presence of a value in the structure's instances, before checking the equality of the values themselves, and does not result in the packaging of the structure's instances into objects .
    • What is characteristic, in this case, comparing an instance of a Nullable (Of T) structure with an untyped null also results in calling the operator T. == (T, T) or T.! = (T, T), while a similar comparison of the instance of the structure Nullable (Of T) , which does not have overloaded operators T. == (T, T) and T.! = (T, T), calls the operator Object. == (Object, Object) or Object.! = = Object , Object) and, as a result, to the packaging of an instance of the structure object .

  3. The PersonStruct.Equals (PersonStruct) method (implementation of IEquatable (Of PersonStruct)) is implemented by calling the PersonStruct.Equals method (PersonStruct, PersonStruct).


  4. To prevent the packing of structure instances into an object , if one or two Nullable (Of PersonStruct) instances participate in the comparison, the following are implemented:


Note:



For structures, the exhaustive implementation of comparing instances by value turned out to be much simpler and more compact due to the lack of inheritance in User defined structs, and also due to the absence of the need for checks for null .
(However, compared with the implementation for classes, a new logic has appeared that supports the Nullable (Of T) -arguments).


In the next publication we will summarize the cycle on the topic "Object Equality", incl. Consider:



')

Source: https://habr.com/ru/post/319100/


All Articles