I need to have instances of a DispenseFile class that inherits from DispenseEntity that implements IDispenseEntity use a custom equality for purposes of comparing elements in List.
My interface is:
public interface IDispenseEntity : IEquatable<IDispenseEntity>
{
byte[] Id { get; set; }
List<byte[]> Key { get; }
List<byte[]> ParentKey { get; set; }
double Volume { get; set; }
public bool DispenseEnabled { get; set; }
string Name { get; set; }
}
My DispenseEntity class:
public class DispenseEntity : IDispenseEntity, IEquatable<IDispenseEntity>
{
//Other properties and methods
//I've tried - this implements IEquatable:
public bool Equals(IDispenseEntity other)
{
return Id.SequenceEqual(other.Id);
}
//I've tried - this overrides default:
public override bool Equals(object obj)
{
return Id.SequenceEqual((obj as IDispenseEntity).Id);
}
}
My DispenseFile class:
public class DispenseFile : DispenseEntity, IParent, IOutputable, IDispenseFile
{
//Other methods and properties
public override bool Equals(object obj)
{
return base.Equals(obj);
}
}
No matter what I use in DispenseEntity class for Equals() method it does not get used when I try:
List<IDispenseEntity> before = _aList;
List<IDispenseEntity> after = _bList;
var intersect = before.Intersect(after).ToList();
The intersect list has zero elements.
I am absolutely positive that both _aList and _bList have an instance of DispenseFile that inherits DispenseEntity who implements IDispenseEntity. I have written test code that finds the only DispenseFile entity in _aList and finds a single instance of DispenseFile in _bList. Both of these instances are created separately and have identical property Id ( new byte[] {1,2,3,4}
)
I have tried overriding Equals. I have tried adding IEquatable to the base class and implementing the equals and GetHashCode and those don't get used.
The problem has to be me, what am I doing wrong?
You don't even need to implement IEquatable anything; overriding GetHashCode and Equals will suffice
public class DispenseEntity : IDispenseEntity
{
...
public override int GetHashCode()
{
return new BigInteger(Id).GetHashCode(); //or your choice
}
public override bool Equals(object obj)
{
return obj is DispenseEntity x && x.Id.SequenceEqual(Id);
}
}
No matter what I use in DispenseEntity class for Equals() method it does not get used
Indeed it might not; a précis on Intersect:
Intersect uses a HashSet; the contents of list B are added to a set, then the contents of A are enumerated and attempted to be added to the set. If the Add returns false, indicating the item is already known, the item from A is yielded. At the end of the operation all those items from A that are also in B have been returned
Now, for a HashSet using the default hashcode provider to decide if it contains some object X it first gets X's hashcode and looks in the objects it knows about to see if there are any other objects with the same hashcode.
If there are no known objects with the same hash, then it deems that the set doesn't contain the object.
If there are object(s) with the same hash, then it uses Equals to decide if the colliding object truly is the same. If you rely on the default hashcode implementation from object it's essentially the memory address of the item, so the only way you'd get the same hashcode is if list A and list B share an instance
Long story short, if you don't override GetHashCode you'll get an empty set result because when all of _bList
is added to the set, and then all of _aList
is enumerated and the set is asked "got this one?" the answer is always "no" - Equals never needs to be used to figure out if instances are the same because the hashes are always different, and the intersection is { }
(nothing)
..but if you've got Equals and GetHashCode overridden, you should be good to go. You could even override GetHashCode to return 1
(don't; it would be terribly inefficient) and you'd see Equals used (a lot)..