0

If we have a class that contains properties/fields of other reference types, how would a proper and elegant equality check be performed?

For example, MainClass contains properties of SubClassA and SubClassB. We can assume that all of these classes override object.Equals with the same implementation, what would that be?

I came up with the following which doesn't look very nice. My logic is as follows:

  1. We cannot use Equals directly on the reference types because they can be null, so we first check if only one of them is null, which leads to inequality.

  2. Then, if both have a value, make sure the value is equal.

  3. We can omit the check that they are both null as it leads to equality.

    class MainClass {
        SubClassA A {get; set;}
        SubClassB B {get; set;}
        int someint {get; set;}
    
        public override bool Equals(object obj) {
            MainClass other = obj as MainClass;
            if (other is null)
                return false;
    
            // First check if only one of the As is null.
            if (A is null ^ other.A is null) {
                return false;
            }
            // Then check if both have a value that the value is equal.
            else if (A != null && other.A != null && !A.Equals(other.A)) {
                return false;
            }
            // Case where As are both null is omitted as it leads to equality.
    
            // Same principle here and for any other reference types...
            if (B is null ^ other.B is null) {
                return false;
            }
            else if (B != null && other.B != null && !B.Equals(other.B)) {
                return false;
            }
    
            // Value types can go down here.
            return someint.Equals(other.someint);
        }
    }
    
    class SubClassA {
        // some properties
    }
    
    class SubClassB {
        // some properties
    }
    
3
  • Don't just implement an Equals() override. Implement the equals operator == and != too. SubClassA.Equals() should do a null check for other. SubClassA.operator == should do a null check for the left side (that's possible because it's static). Now you can reduce the if/else if for each reference type property to: if (A != other.A) return false; or even better just chain multiple of them with &&. Commented Feb 15, 2024 at 10:58
  • Implementing equality is a little tricky to do correctly and involves the IEquatable interface, overriding Equals(object), implementing == and !=, as well as overriding GetGashCode(). It's lots of annoying boilerplate code. But if you use records instead of classes the compiler will implement it all for you. Have you considered that? Commented Feb 15, 2024 at 11:05
  • The first comment was something I thought would be the solution, i.e. the null checks will be able to be avoided somehow. As for using records, it is probably possible for most cases where intances don't have an id field, which should not be accounted for in the equality check. Sadly, I am tied to C# 7.2 right now. Commented Feb 15, 2024 at 11:30

2 Answers 2

1

You don't need to reinvent the wheel. .NET has an EqualityComparer<T>.Default property that can handle most of equality comparison situations (including yours). Read the remark section for more details.

EqualityComparer<SubClassA>.Default.Equals(A, other.A);

Note, please read this article: System.Object.Equals method if you want to override Equals. In summary, you'd better override the GetHashCode method and implement the IEquatable<T> interface too.

Sign up to request clarification or add additional context in comments.

2 Comments

The docs state that EqualityComparer<T>.Default will use the IEquatable<T> equality check if possible, then Object.Equals. Doesn't that mean in my case that I will have to implement IEquatable<T> for all subclasses first in order to be able to use it in MainClass?
That means if IEquatable<T> is not implemented, it will use Object.Equals.
0

If the class/subclass is made up of only POCO or serializable objects, then serialize and compare the strings. Any other solution would require you to override Equals() and GetHashCode(). It is not worth the pain. Rather, create a specific method (say, "ValueEquals()") that compares specific members of the two objects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.