#423 – Compare Two Lists Using SequenceEquals

If you use either the == operator or the Equals method of the List<T> type to compare two lists, the return value will be true only if you are comparing two references to the exact same List<T> instance.  List<T> does not override either the == operator or the Equals method, so the comparison falls back to the reference equality check performed by Object.Equals.

            List<int> myPrimes = new List<int> { 2, 3, 5, 7, 11, 13, 17 };
            List<int> yourPrimes = new List<int> { 2, 3, 5, 7, 11, 13, 17 };

            // == and Equals : reference equality
            bool compare = (myPrimes == yourPrimes);        // false
            compare = myPrimes.Equals(yourPrimes);     // false

If you want to compare two lists by comparing the values of the objects in the list, you can use the SequenceEqual method provide by System.Linq.

            // SequenceEquals method : value equality
            compare = myPrimes.SequenceEqual(yourPrimes);  // true

            List<int> dougsPrimes = new List<int> { 2, 3, 5, 7, 11, 13, 19 };  // oops
            compare = yourPrimes.SequenceEqual(dougsPrimes);  // false
Advertisements

#422 – How ReferenceEquals Behaves When Comparing Strings

The Object.ReferenceEquals method uses reference equality semantics, returning true if two variables refer to the same object in memory.

When comparing strings, you typically want value equality semantics, so you would not use ReferenceEquals.  In the example below, the string values are the same, but the strings are stored in different memory locations, so ReferenceEquals returns false.

            string s1 = "Bouffant";
            StringBuilder sb2 = new StringBuilder("Bouffant");
            bool compare = ReferenceEquals(s1, sb2.ToString());  // false

Because of the way that the compiler stores strings, ReferenceEquals might return true for two equivalent string constants stored in two different variables.  In the example below, the compiler stores just one copy of the string “Galoshes”.

            string s1 = "Galoshes";
            string s2 = "Galoshes";
            bool compare = ReferenceEquals(s1, s2);  // true

Even though ReferenceEquals returns true in this case, you should not rely on this behavior, but use Equals or the == operator to compare two strings.

#421 – Value Equality and IComparable Example

Here’s a full example of a reference type that supports value equality semantics and implements both IEquatable and IComparable.

    public class Rectangle : IEquatable<Rectangle>, IComparable<Rectangle>
    {
        public int Height { get; set; }
        public int Width { get; set; }

        public Rectangle(int height, int width)
        {
            Height = height;
            Width = width;
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as Rectangle);
        }

        public override int GetHashCode()
        {
            return Height.GetHashCode() ^ Width.GetHashCode();
        }

        public bool Equals(Rectangle r)
        {
            if (ReferenceEquals(r,null))
                return false;

            return ((Height == r.Height) && (Width == r.Width) ||
                    (Height == r.Width) && (Width == r.Height));
        }

        public static bool operator ==(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
            {
                return ReferenceEquals(r2, null) ? true : false;
            }

            return r1.Equals(r2);
        }

        public static bool operator !=(Rectangle r1, Rectangle r2)
        {
            return !(r1 == r2);
        }

        // Result:
        //  < 0 : this instance less than r
        //  = 0 : this instance equivalent to r
        //  > 0 : this instance greater than r
        public int CompareTo(Rectangle r)
        {
            if (ReferenceEquals(r, null))
                return 1;

            if (this.Equals(r))
                return 0;

            else if (this.Area() == r.Area())
                return this.Width - r.Width;

            else
                return this.Area() - r.Area();
        }

        public static bool operator <(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) < 0) ? true : false;
        }

        public static bool operator >(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) > 0) ? true : false;
        }

        public static bool operator <=(Rectangle r1, Rectangle r2)
        {
            return (r1 < r2) || (r1 == r2);
        }

        public static bool operator >=(Rectangle r1, Rectangle r2)
        {
            return (r1 > r2) || (r1 == r2);
        }

        public int Area()
        {
            return Height * Width;
        }
    }

#420 – Laundry List for Implementing Value Equality and IComparable

To implement value equality (equivalence) in a reference type, you should do the following:

  • Override Object.Equals
    • In Object.Equals, call the type-specific Equals method using the as operator
  • Implement IEquatable<T> by adding a type-specific Equals method
    • Check for null using ReferenceEquals method
    • Call base class’ Equals method to compare fields that exist in the base class if it also checks for value equality
    • Check for equivalence by comparing individual fields
  • Override GetHashCode, generating a hash code based on fields used for equivalence
  • Overload == operator
    • Check for 1st parameter being null, compare to 2nd parameter
    • Call 1st parameter’s type-specific Equals method
  • Overload != operator, invoking the == operator
  • Implement IComparable<T>, adding a CompareTo method
    • Check for equivalence using Equals method, then compare individual fields
  • Overload < and > operators
    • Check for 1st parameter being null
    • Call 1st parameter’s CompareTo method
  • Overload <= and >= operators
    • Calculate a result using the <, > and == operators

#419 – Override Relational Operators When You Implement IComparable

When a class implements IComparable, it must implement the CompareTo method.  For completeness, you should also override the relational operators.

Here’s an example.

    public class Rectangle : IEquatable<Rectangle>, IComparable<Rectangle>
    {
        public int Height { get; set; }
        public int Width { get; set; }

        public Rectangle(int height, int width)
        {
            Height = height;
            Width = width;
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as Rectangle);
        }

        public override int GetHashCode()
        {
            return Height.GetHashCode() ^ Width.GetHashCode();
        }

        public bool Equals(Rectangle r)
        {
            if (ReferenceEquals(r,null))
                return false;

            return ((Height == r.Height) && (Width == r.Width) ||
                    (Height == r.Width) && (Width == r.Height));
        }

        public static bool operator ==(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
            {
                return ReferenceEquals(r2, null) ? true : false;
            }

            return r1.Equals(r2);
        }

        public static bool operator !=(Rectangle r1, Rectangle r2)
        {
            return !(r1 == r2);
        }

        // Result:
        //  < 0 : this instance less than r
        //  = 0 : this instance equivalent to r
        //  > 0 : this instance greater than r
        public int CompareTo(Rectangle r)
        {
            if (ReferenceEquals(r, null))
                return 1;  

            if (this.Equals(r))
                return 0;

            else if (this.Area() == r.Area())
                return this.Width - r.Width;

            else
                return this.Area() - r.Area();
        }

        public static bool operator <(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) < 0) ? true : false;
        }

        public static bool operator >(Rectangle r1, Rectangle r2)
        {
            if (ReferenceEquals(r1, null))
                return false;

            else
                return (r1.CompareTo(r2) > 0) ? true : false;
        }

        public static bool operator <=(Rectangle r1, Rectangle r2)
        {
            return (r1 < r2) || (r1 == r2);
        }

        public static bool operator >=(Rectangle r1, Rectangle r2)
        {
            return (r1 > r2) || (r1 == r2);
        }

        public int Area()
        {
            return Height * Width;
        }
    }

#418 – Implementing the IComparable Interface

If you implement a reference type where it makes sense to compare two instances of the type, with one instance greater or lesser than another, you should implement the IComparable interface for your type.  IComparable is used within .NET when sorting elements of a list, e.g. List<T>.Sort.

Here’s an example for a Rectangle type.

        // Result:
        //  < 0 : this instance less than r
        //  = 0 : this instance equivalent to r
        //  > 0 : this instance greater than r
        public int CompareTo(Rectangle r)
        {
            if (this.Equals(r))
                return 0;

            else if (this.Area() == r.Area())
                return this.Width - r.Width;

            else
                return this.Area() - r.Area();
        }

#417 – Provide a Type-Specific Equals Method for Value Equality

When you are implementing value equality in a type, you typically override the Equals method that is defined in System.Object.  It has the following signature:

        public override bool Equals(object obj)

You should also add a type-specific Equals method.  For completeness, you can indicate that your class implements IEquatable<T>, which includes the type-specific Equals method.

    public class Dog : IEquatable<Dog>

Below is a complete example, showing us the override of System.Object.Equals, as well as the type-specific Equals method.  Note that the generic Equals method calls the type-specific version.

        // System.Object.Equals
        public override bool Equals(object obj)
        {
            return this.Equals(obj as Dog);
        }

        // IEquatable<Dog>.Equals
        public bool Equals(Dog d)
        {
            if (d == null)
                return false;

            return (Name == d.Name) && (Age == d.Age);
        }