-2

I'm trying to implement IEqueatable so I could use .Except in my custom type LINQ queries.

The custom type code looks like this:

public class Case : IEquatable<Case>
    {
        [Key]
        public int Id { get; set; }

        //More properties
        [...]      

        public bool Equals(Case other)
        {
            // Check whether the compared object references the same data.
            if (ReferenceEquals(this, other)) return true;

            // Check whether the compared object is null.
            if (ReferenceEquals(other, null)) return false;

            // Check whether the objects’ properties are equal.
            return Id.Equals(other.Id);
        }

        public override bool Equals(object obj)
        {
            var other = obj as Case;
            // Check whether the compared object references the same data.
            if (ReferenceEquals(this, other)) return true;

            // Check whether the compared object is null.
            if (ReferenceEquals(other, null)) return false;

            // Check whether the objects’ properties are equal.
            return Id.Equals(other.Id);
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        public static bool operator ==(Case case1, Case case2)
        {
            if ((object)case1 == null || (object)case2 == null)
                return Equals(case1, case2);
            return case1.Equals(case2);

        }

        public static bool operator !=(Case case1, Case case2)
        {
            if ((object)case1 == null || (object)case2 == null)
                return !Equals(case1, case2);
            return !case1.Equals(case2);
        }
    }

I've added throw new NotImplementedException(); in the Equals method but it never gets called.

I've followed the conventions shown here: https://msdn.microsoft.com/en-us/library/ms131190.aspx

and here https://blogs.msdn.microsoft.com/csharpfaq/2009/03/25/how-to-use-linq-methods-to-compare-objects-of-custom-types/

But no success.

EDIT

Here's the code that calls for the Except method:

 if (Checkset(set))
                    {
                        var subset = GetPowerSet(set);
                        var newset = powerset.Except(subset);
                    }

Where both powerset and subset are array of Case.

RainierMallol
  • 806
  • 1
  • 8
  • 24

4 Answers4

0

Before you try doing things with your linq queries, I'd recommend playing with something simpler. I.E.

var case1_1 = new Case() { Id = 1 };
var case1_2 = new Case() { Id = 1 };

var areEqual = case1_1 == case1_2;

With that test in place, these are the reasons I can think of why you didn't hit that Exception:

  1. you have 2 Equals methods, and you only threw the exception from 1 of them.
  2. No 2 objects you've compared have the same Id. If the result of GetHashCode is different, it might not even attempt to compare the values.
  3. The exception was caught
  • Hi, thanks for your answer. I have done multiple tests and: 1. First of all, having 2 equals methods is the way the IEqueatble should be implemented, one overriding the previous signature of the class, and the other specifying a custom comparison using the type Case. This is suggested in the MSDN link I provided as reference. The exception is never thrown. 2. I've also add the throw new Exception in the GetHashCode, which is not evaluated. I know for a fact the cases I'm comparing match. 3. No exception was caught. – RainierMallol Jul 26 '16 at 22:21
  • @RainierMallol I wasn't saying that you shouldn't have 2 equals methods. I was just saying that you might have been checking in the wrong one. – Sam I am says Reinstate Monica Jul 26 '16 at 22:26
0

I'd be interested to know whether you tried trapping in both overloads for Equals ... the only thing that pops out from your code is that you have duplicate implementations for checking equality. Ideally you'd only do that in one place.

Here's a sample implementation ... you would replace EntityBase with Case ...

  /// <summary>
  ///    Indicates whether the current object is equal to another object of the same type.
  /// </summary>
  /// <returns> true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false. </returns>
  /// <param name="other"> An object to compare with this object. </param>
  public bool Equals( EntityBase other ) {
     return !ReferenceEquals(other, null)
            && Id.Equals(other.Id);
  }

  /// <summary>
  ///    Serves as a hash function for a particular type.
  /// </summary>
  /// <returns> A hash code for the current <see cref="T:System.Object" />. </returns>
  /// <filterpriority> 2 </filterpriority>
  public override int GetHashCode() {
     return Id.GetHashCode();
  }

  /// <summary>
  ///    Determines whether the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />.
  /// </summary>
  /// <returns> true if the specified object is equal to the current object; otherwise, false. </returns>
  /// <param name="obj"> The object to compare with the current object. </param>
  /// <filterpriority> 2 </filterpriority>
  public override bool Equals( object obj ) {
     return Equals(obj as EntityBase);
  }

  /// <summary>
  ///    Determines if the <paramref name="left" /> instance is considered equal to the <paramref name="right" /> object.
  /// </summary>
  /// <param name="left"> The instance on the left of the equality operator. </param>
  /// <param name="right"> The instance on the right of the equality operator. </param>
  /// <returns> True if the instances are considered equal, otherwise false. </returns>
  public static bool operator ==( EntityBase left, EntityBase right ) {
     return ReferenceEquals(left, null)
        ? ReferenceEquals(right, null)
        : left.Equals(right);
  }

  /// <summary>
  ///    Determines if the <paramref name="left" /> instance is considered unequal to the <paramref name="right" /> object.
  /// </summary>
  /// <param name="left"> The instance on the left of the inequality operator. </param>
  /// <param name="right"> The instance on the right of the inequality operator. </param>
  /// <returns> True if the instances are considered unequal, otherwise false. </returns>
  public static bool operator !=( EntityBase left, EntityBase right ) {
     return !(left == right);
  }
Peter-John
  • 71
  • 7
  • Hi Peter John, thanks for your message. Your code is missing an implementation of IQuetable.Equals(EntityBase). I've added it to my code and I'm still getting the same results. – RainierMallol Jul 27 '16 at 00:50
0

If you only call Except:

var exceptList = list1.Except(list2);

No list is returned, to execute the comparation you need to enumerate the results of the Except, with a foreach for example:

foreach(var listElement in exceptList) 
{
    //...
}

Then the GetHashCode and Equals methods are called.

EDIT: This code does an Except of two lists:

    static void Main(string[] args)
    {
        List<MyClass> list1 = new List<MyClass>();

        MyClass myClass1 = new MyClass() {Id = 1};
        MyClass myClass2 = new MyClass() {Id = 2};

        list1.Add(myClass1);
        list1.Add(myClass2);

        List<MyClass> list2 = new List<MyClass>();
        list2.Add(myClass1);

        var exceptList = list1.Except(list2);

        foreach (var myClass in exceptList)
        {
            Console.WriteLine(myClass.Id);
        }
    }


    public class MyClass : IEquatable<MyClass>
    {
        public int Id { get; set; }

        public bool Equals(MyClass other)
        {
            return Id == other.Id;
        }

        public override int GetHashCode()
        {
            return Id;
        }
    }

This code prints "2" in the console

Ruben Aguilar
  • 1,205
  • 11
  • 19
  • Hi, what happens is that the Except list is not working at all, so the value of exceptList in this case would be the same as list1. – RainierMallol Jul 26 '16 at 22:05
  • Thanks for posting this but, I've tried it this way and still the behaviour is the same. I know for a fact that the objects in the subset are IN the old list, as it is a powerset of one of the old list objects. A powerset is a set that contains all possible combinations of items in a set. So, the old set is a previous powerset of a X item, and the new set is one powerset of the old set, meaning that all of those items are in the old list, hence, in my BL, they need to be deleted. – RainierMallol Jul 27 '16 at 00:45
0

Equals is only called when necessary. All LINQ methods working on IEquatable<T> implementations first use (and store) the hash codes of the objects they process. These methods only call Equals if two object have the same hash code.

In your data there is no case with identical Ids, so it's never necessary to call Equals.

This can easily be demonstrated by an example. If you have three lists:

var list1 = new List<Case>
{
    new Case{ Id = 1 },
    new Case{ Id = 2 },
    new Case{ Id = 3 }
};
var list2 = new List<Case>
{
    new Case{ Id = 4 },
    new Case{ Id = 5 },
    new Case{ Id = 6 }
};
var list3 = new List<Case>
{
    new Case{ Id = 1 },
    new Case{ Id = 5 },
    new Case{ Id = 6 }
};

Then the following statement (with some trace statements in Equals and GetHashCode, and the latter simply returning Id) ...

list1.Except(list2).ToList();

... will output:

GetHashCode: 4
GetHashCode: 5
GetHashCode: 6
GetHashCode: 1
GetHashCode: 2
GetHashCode: 3

While the statement ...

list1.Except(list3).ToList();

... will output:

GetHashCode: 1
GetHashCode: 5
GetHashCode: 6
GetHashCode: 1
Equals: 1 - 1
GetHashCode: 2
GetHashCode: 3
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291