#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
Advertisement

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

#416 – Use an Epsilon to Compare Two Floating Point Numbers

Because of problems with the precision of floating point numbers, two numbers that would ought to be considered equal might actually have different values.  Because of this, you typically don’t want to use the == operator on floating point numbers, when checking for equality.  Instead, you likely want to see if the difference between the two numbers is small enough that the numbers can be considered equal.

Here’s helper function that determines if two float values are nearly equal.

        public static bool NearlyEqual(float f1, float f2)
        {
            // Equal if they are within 0.00001 of each other
            return Math.Abs(f1 - f2) < 0.00001;
        }

We then get the following results:

            float f1 = 1.0f / 6.0f;
            float f2 = (f1 * 6.0f) - 1.0f;   // Should be 0.0
            float f3 = 0.0f;

            bool check = (f2 == f3);       // false !
            check = NearlyEqual(f2, f3);   // true


The value that you choose for epsilon will typically depend on your application.

#415 – Be Careful When Checking Floating Point Numbers for Equality

When you check two floating point numbers for equality in C#, you might sometimes be surprised at the result.

Consider the following example.  f2 is equal to six sixths (1.0), minus 1.0–which should be equal to 0.0.  But when we compare the result to 0.0, we see that the values are not equal.

            float f1 = 1.0f / 6.0f;
            float f2 = (f1 * 6.0f) - 1.0f;   // Should be 0.0
            float f3 = 0.0f;

            bool check = (f2 == f3);    // Should be true, but is false !

This happens because floating point numbers can’t necessarily be represented exactly when stored in a floating point variable.  Instead, the value stored can be very close, but not equal to, the desired value.

Here’s the output from the example above.  f2 should be equal to 0.0, after our calculation.  It is instead very nearly equal to 0.0.  Because of this, the comparison to the constant 0.0 fails.

#414 – Equivalence Can Be Based on a Subset of Fields

When you define value equality for a type, you typically compare all fields in the two instances to determine whether they are equivalent.  You can also use just a subset of the fields in the comparison.

Below, two Programmer instances are equivalent if their Name and Age properties match.  But their Mood properties are not used in the comparison.

    public class Programmer
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Mood { get; set; }

        // constructor omitted

        public static bool operator ==(Programmer p1, Programmer p2)
        {
            return (p1.Name == p2.Name) && (p1.Age == p2.Age);
        }

        public static bool operator !=(Programmer p1, Programmer p2)
        {
            return !(p1 == p2);
        }
    }

Test results:

            Programmer p1 = new Programmer("Sean", 47, "Elated");
            Programmer p2 = new Programmer("Sean", 47, "Surly");
            Programmer p3 = new Programmer("Bob", 47, "Surly");

            bool check = (p1 == p2);        // true
            check = (p2 == p3);             // false