Miguel Moura
Miguel Moura

Reputation: 39384

Two lists with same elements are not equal. Why?

I have the following:

  var a = new List<OrderRule> {
    new OrderRule("name", OrderDirection.Ascending),
    new OrderRule("age", OrderDirection.Descending)
  };


  var b = new List<OrderRule> {
    new OrderRule("name", OrderDirection.Ascending),
    new OrderRule("age", OrderDirection.Descending)
  };

  var r = a.Equals(b);

The r variable is false even if the two lists include items which are equal to each other. The OrdeRule class implements IEquality. Note that two OrderRules are equal when both Direction and Property are equal.

public enum OrderDirection { ASC, DESC }

public class OrderRule : IEquatable<OrderRule> {

  public OrderDirection Direction { get; }
  public String Property { get; }

  public OrderRule(String property, OrderDirection direction) {
    Direction = direction;
    Property = property;
  }

  public Boolean Equals(OrderRule other) {

    if (other == null)
      return false;

    return Property.Equals(other.Property) && Direction.Equals(other.Direction);

  }

  public override Boolean Equals(Object obj) {

    if (ReferenceEquals(null, obj))
      return false;

    if (ReferenceEquals(this, obj))
      return true;

    if (obj.GetType() != GetType())
      return false;

    return Equals(obj as IncludeRule);

  }

  public override Int32 GetHashCode() {

    return HashCode.Of(Property).And(Direction);

  }

}

What am I missing?

Upvotes: 5

Views: 4376

Answers (4)

Scott Hannen
Scott Hannen

Reputation: 29222


Checking to see if two lists are equal doesn't mean checking to see if they both contain the same items.

If you do this:

var a = new List<OrderRule>();
var b = new List<OrderRule>();
var r = a.Equals(b); //false

r will always be false, even if the two lists have the same items in them. That's because you're not checking the contents of the lists. You're actually checking to see if both a and b refer to the same object. But a is one object and b is another.

If you did this, r would be true:

var a = new List<OrderRule>();
var b = a;
var r = a.Equals(b) //true

If you have two lists and you want to see if they contain the same items in the same order, you could do this:

var r = a.SequenceEquals(b);

In the example in your question r would be true because you're overriding Equals on OrderRule, so the items in the one list are considered "equal" to the items in the other.


Got it - your equality check in OrderRule is broken.

public override Boolean Equals(Object obj) {
if (ReferenceEquals(null, obj))
  return false;

if (ReferenceEquals(this, obj))
  return true;

if (obj.GetType() != GetType())
  return false;

return Equals(obj as IncludeRule);
}

I think instead of return Equals(obj as IncludeRule) you mean return Equals(obj as OrderRule).

Upvotes: 9

pm100
pm100

Reputation: 50190

your code is testing to see if they are the same list. not if they have the same contents.

a.Equals(b); // false
var d = a;
d.Equals(a); // true

if you want to compare the contents then use linq's SequenceEqual

https://msdn.microsoft.com/en-us/library/bb348567(v=vs.100).aspx

Upvotes: 4

Sukrat Kashyap
Sukrat Kashyap

Reputation: 21

First whenever one uses new keyword or creates a new object the reference or the address of the object is stored in the variable. Also equals compares the value of the variable.

Now you have overrided the equals function of the OrderRule. So if you do equals on two OrderRule you will get the result as you compare it inside the overrided equals function.

But now think about this What is List<OrderRule> its nothing but a generic class. So again the equals will check the value of the variable which contains the reference and since they are different you will not get true when you compare them. Also List implements the equals same as that of the object its not overrrided. Hence one way which i prefer is to create an extensions.

public static class Extensions
{
    public static bool Equal<T>(this List<T> x, List<T> y)
    {
        bool isEqual = true;
        if (x == null ^ y == null)
        {
            isEqual = false;
        }
        else if (x == null && y == null)
        {
            isEqual = true;
        }
        else if (x.Equals(y))
        {
            isEqual = true;
        }
        else if (x.Count != y.Count)
        {
            isEqual = false;
        }
        else
        {
            //This logic can be changed as per your need.
            //Here order of the list matters!
            //You can make one where order doesn't matter
            for (int i = 0; i < x.Count; i++)
            {
                if (!x[i].Equals(y[i]))
                {
                    break;
                }
            }

        }
        return isEqual;
    }
}

Upvotes: 1

easy
easy

Reputation: 318

Doing so you are comparing references but not objects. So you get inequality returned.

Upvotes: 0

Related Questions