Comparing values for equality is a fundamental aspect of C# programming. While comparing built-in types like numbers, strings, and DateTime
is straightforward, comparing custom objects requires a deeper understanding. This article explores how to define and implement custom equality comparisons in C#.
Default Equality Comparison in C
By default, C# compares objects based on reference equality. This means two object variables are considered equal only if they refer to the same memory location. Every class implicitly inherits from the Object
class, which provides the Equals()
method for equality comparison. The default implementation of Equals()
checks for reference equality, equivalent to the ReferenceEquals()
method.
var person = new Person { Name = "Jay", Age = 25 };
var samePerson = person;
var newPerson = new Person { Name = "Jay", Age = 25 };
Console.WriteLine(person.Equals(samePerson)); // true, same instance
Console.WriteLine(person == samePerson); // true, same instance
Console.WriteLine(person.Equals(newPerson)); // false, different instances
Console.WriteLine(person == newPerson); // false, different instances
In this example, person
and samePerson
are equal because they point to the same object. However, person
and newPerson
are not equal, despite having identical property values, because they are different instances. This default behavior is often insufficient when comparing custom objects based on their content.
Implementing Custom Equality Comparison
To compare objects based on their content rather than reference, you need to override the Equals()
method and overload the ==
and !=
operators. Let’s illustrate this with a Vehicle
class:
public class Vehicle
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public override bool Equals(object? obj)
{
if (obj is null || obj is not Vehicle) return false;
var otherVehicle = (Vehicle)obj;
return Make == otherVehicle.Make && Model == otherVehicle.Model && Year == otherVehicle.Year;
}
public static bool operator ==(Vehicle x, Vehicle y) => x.Equals(y);
public static bool operator !=(Vehicle x, Vehicle y) => !x.Equals(y);
}
Now, comparing two Vehicle
objects will check for equality based on the values of Make
, Model
, and Year
.
var vehicle1 = new Vehicle { Make = "Toyota", Model = "Camry", Year = 2024 };
var vehicle2 = new Vehicle { Make = "Toyota", Model = "Camry", Year = 2024 };
Console.WriteLine(vehicle1 == vehicle2); // true
Overriding GetHashCode()
When overriding Equals()
, it’s crucial to also override GetHashCode()
. This method generates a numerical hash code representing the object’s state. Objects that are equal according to Equals()
must have the same hash code. A simple implementation using XOR:
public override int GetHashCode()
{
return (Make?.GetHashCode() ?? 0) ^ (Model?.GetHashCode() ?? 0) ^ Year.GetHashCode();
}
This ensures consistency between equality comparison and hash code generation. Using the null-coalescing operator (??
) handles potential null values for string properties.
Conclusion
Custom equality comparison in C