Implementing EqualityCompare vs overriding GetHashCode and Equals - c#

I created two classes almost identical. Both represent a Pair (x,y) but in one of them I overrode the GetHashCode and Equals methods. I was told that when the HashCode is different the Collections takes them as different elements and does not even bother to actually compare them with the equals. However, it turns out that I implemented an EqualityComparer for the class that do not override the GetHashCode and Equals and everything works fine even when the HashCodes are still different.
Take a look at my Console Project:
using System;
using System.Collections.Generic;
using System.Linq;
namespace matrixExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Same Hash but no insertion: as expected");
HashSet<MyPair> hash = new HashSet<MyPair>();
MyPair one = new MyPair { X = 10, Y = 2 };
MyPair copyOfOne = new MyPair { X = 10, Y = 2 };
Console.WriteLine(one.GetHashCode() + " " + hash.Add(one));
Console.WriteLine(copyOfOne.GetHashCode() + " " + hash.Add(copyOfOne));
Console.WriteLine("-----------------------------------------");
Console.WriteLine("Different Hash but no insertion! why?");
HashSet<MyPairWithoutOverride> hash2 = new HashSet<MyPairWithoutOverride>(new SameHash());
MyPairWithoutOverride a1 = new MyPairWithoutOverride { X = 10, Y = 2 };
MyPairWithoutOverride a1copy = new MyPairWithoutOverride { X = 10, Y = 2 };
Console.WriteLine(a1.GetHashCode() + " " + hash2.Add(a1));
Console.WriteLine(a1copy.GetHashCode() + " " + hash2.Add(a1copy));
}
public class MyPair
{
public int X { get; set; }
public int Y { get; set; }
public override int GetHashCode()
{
return X * 10000 + Y;
}
public override bool Equals(object obj)
{
MyPair other = obj as MyPair;
return X == other.X && Y == other.Y;
}
}
public class MyPairWithoutOverride
{
public int X { get; set; }
public int Y { get; set; }
}
public class SameHash : EqualityComparer<MyPairWithoutOverride>
{
public override bool Equals(MyPairWithoutOverride p1, MyPairWithoutOverride p2)
{
return p1.X == p2.X && p1.Y == p2.Y;
}
public override int GetHashCode(MyPairWithoutOverride i)
{
return base.GetHashCode();
}
}
}
}

Your problem is here
public override int GetHashCode(MyPairWithoutOverride i)
{
return base.GetHashCode();
}
You're returning base.GetHashCode() which is actually the hash code of the SameHash class. So you actually are returning the same hash code every single time.
If you return i.GetHashCode() then it will behave as expected.

Related

Contains Returning False If Exist in List

I'm running into a problem where the contains method is return false even though it exist in the list. Can someone tell me what's wrong with this?
Program.CS
points.Add(new Point(-4, -7));
points.Add(new Point(0, 0));
points.Add(new Point(1, 2));
points.Add(new Point(-4, 5));
points.Insert(2, new Point(3, 1));
points.Add(new Point(7, -2));
points[0] = new Point(2, 1);
points.RemoveAt(2);
bool returnPoint = false;
returnPoint = points.Contains(new Point(1, 2));
PointList.CS
public bool Contains(Point item)
{
return _Points.Contains(item);
}
Point.CS
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
Since Point is a class, it is a reference type. By default, reference types check for referential equality. If you want to get the behavior you're expecting, you should override the Equals and GetHashCode methods in Point, and it probably wouldn't hurt to implement IEquatable<Point> while you're at it, along with the == and != operators.
An example implementation would look like this:
public class Point : IEquatable<Point>
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public bool Equals(Point other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((Point)obj);
}
public override int GetHashCode()
{
unchecked
{
return (X * 397) ^ Y;
}
}
public static bool operator ==(Point left, Point right)
{
return Equals(left, right);
}
public static bool operator !=(Point left, Point right)
{
return !Equals(left, right);
}
}
If that seems like a lot, well, it is. Unfortunately, implementing equality tends to require a good amount of boiler plate in C#. If we get Record types in C# 8 some of the pain may be alleviated, but until then this is the typical approach.
Point is a reference type, so you're unintentionally comparing references, not properties.
You can compare properties by using LINQ's Any() and passing a lambda expression that performs the comparison you wish.
//using System.Linq;
returnPoint = points.Any( p => p.X == 1 && p.Y == 2 );
Contains in ArrayList uses implementation of Equals() on the objects. Currently you are checking if object references are equal.
Point point = new Point(5, 2);
Point refPoint = point; // refer to same object
points.Add(point);
bool returnPoint = false;
returnPoint = points.Contains(refPoint); // return true
Need to override Equals() and GetHashcode in your class for reference types:
public override bool Equals(object other) {
Point otherPoint = other as Point;
if(otherPoint != null)
return (X.Equals(otherPoint.X) && (Y.Equals(otherPoint.Y)));
return false;
}
you need valueObject for best practice solution and For comparison only x and y may not. you should think of functional processes as DDD design
look here
public abstract class ValueObject
{
protected static bool EqualOperator(ValueObject left, ValueObject right)
{
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
{
return false;
}
return ReferenceEquals(left, null) || left.Equals(right);
}
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
{
return !(EqualOperator(left, right));
}
protected abstract IEnumerable<object> GetAtomicValues();
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
var other = (ValueObject)obj;
var thisValues = GetAtomicValues().GetEnumerator();
var otherValues = other.GetAtomicValues().GetEnumerator();
while (thisValues.MoveNext() && otherValues.MoveNext())
{
if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null))
{
return false;
}
if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current))
{
return false;
}
}
return !thisValues.MoveNext() && !otherValues.MoveNext();
}
public override int GetHashCode()
{
return GetAtomicValues()
.Select(x => x != null ? x.GetHashCode() : 0)
.Aggregate((x, y) => x ^ y);
}
}
your PointList.cs
public class PointList
{
public string Bla { get; set; }
public List<Point> Points { get; protected set; } = new List<Point>();
public void AddPoint(int x, int y)
{
AddPoint(new Point(x, y));
}
public void AddPoint(Point p)
{
if (!Points.Any(x => x.Equals(p)))
Points.Add(p);
}
public void RemovePoint(int x, int y)
{
RemovePoint(new Point(x, y));
}
public void RemovePoint(Point point)
{
Points.Remove(point);
}
public Point GetPoint(int x, int y)
{
return GetPoint(new Point(x, y));
}
public Point GetPoint(Point point)
{
return Points.FirstOrDefault(x => x.Equals(point));
}
}
your Point.cs
public class Point : ValueObject
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
protected override IEnumerable<object> GetAtomicValues()
{
yield return X;
yield return Y;
}
}
Examples
class Program
{
static void Main(string[] args)
{
var list = new PointList();
list.AddPoint(1, 1);
list.AddPoint(1, 2);
list.AddPoint(14, 53);
list.RemovePoint(1, 1);
list.RemovePoint(new Point(1, 2));
var p2 = list.GetPoint(14, 53);
var p1 = list.GetPoint(new Point(14, 53));
}
}

Class equal with if on one property

I have a class that I use to describe a XYZ coordinate along with 3 properties.
The class looks like this:
class dwePoint
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
protected bool Equals(dwePoint other)
{ //This doesnt seem to work
if(Prop1== "Keep")
{
return false;
}
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Prop1.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
}
Checking the XYZ on the Equals, I can filter out duplicates only based on the actual coordinates, ignoring the properties.
In my code, I use a list, so I call the List.Distinct()
Now there is one thing I cant figure out yet:
It is possible there are 2 points with the same XYZ, but with different properties.
In that case I always want to keep the one with a specific string (for example "Keep") and always remove the one that has some other value.
I was already trying some if statements, without any luck...
How should I handle this ?
What you want is not really possible with Distinct since it uses your Equals as it's only input for equality (as it should), so it has no way to even be aware that there could be a difference between the objects.
I think it would be a better design for you to compose your class using a new class, e.g. Point3D containing your coordinates and your 3 properties. Then you can group by the point, and for everything that has more than one equal point, apply your own logic as to which to keep.
In code:
class Point3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
// Equals and get hash code here
}
class dwePoint
{
Point3D Coordinate {get;}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Filter list by applying grouping and your custom logic
points = points.GroupBy(p => p.Coordinate)
.Select(x =>
x.OrderByDescending(p => p.Prop1 == "Keep") // Sort so the element you want to keep is first
.First() // If there is only one element, the ordering will not matter
).ToList();
If you really want, the GroupBy also works with your current class design, since only the coordinates takes part in the Equals.
Your GetHashCode runs into a nullref if Prop1 == null, you should fix that.
And another solution: use Aggregate and Lamda to distinct your list. The Equal() on your class only compares the X,Y and Z - the Aggregate lambda makes sure you keep what you want. Probably put the Aggregate in a extension method or Function.
static void Main()
{
List<dwePoint> points = new List<dwePoint>();
// Testdata
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
for (int z = 0; z < 3; z++)
{
points.Add(new dwePoint { X = x, Y = y, Z = z });
if (x == y && x == z) // and some duplicates to Keep
points.Add(new dwePoint { X = x, Y = y, Z = z, Prop1 = "Keep" });
}
// prefer the ones with "Keep" in Prop1
var distincts = points.Aggregate(new HashSet<dwePoint>(), (acc, p) =>
{
if (acc.Contains(p))
{
var oldP = acc.First(point => point.X == p.X && point.Y == p.Y && point.Z == p.Z);
if (oldP.Prop1 == "Keep")
{
// do nothing - error, second point with "keep"
}
else
{
acc.Remove(oldP);
acc.Add(p); // to use this ones other props later on ....
}
}
else
acc.Add(p);
return acc;
}).ToList();
Console.WriteLine(string.Join(" - ", points));
Console.WriteLine(string.Join(" - ", distincts));
Console.ReadLine();
}
private class dwePoint
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
public override string ToString() => $"{X}-{Y}-{Z}-{Prop1}-{Prop2}-{Prop3}";
protected bool Equals(dwePoint other)
{
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
}
I had the same answer in mind as driis provided..
But, I would like to provide you with another option.
You can write a Distinct on your own as an extension method.
Here is a work around. I wrote a sample code, so that you can understand better.
static class Program
{
static void Main(string[] args)
{
List<Abc> list = new List<Abc>()
{
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
new Abc()
{
a = 5,
b = 6,
s = "Keep"
},
new Abc()
{
a = 9,
b = 10,
s = "Keep"
},
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
};
list = list.MyDistinct();
}
// Extension Method
public static List<Abc> MyDistinct(this List<Abc> list)
{
List<Abc> newList = new List<Abc>();
foreach (Abc item in list)
{
Abc found = newList.FirstOrDefault(x => x.Equals(item));
if (found == null)
{
newList.Add(item);
}
else
{
if (found.s != "Keep" && item.s == "Keep")
{
newList.Remove(found);
newList.Add(item);
}
}
}
return newList;
}
}
class Abc
{
public int a, b;
public string s;
public override bool Equals(object obj)
{
Abc other = obj as Abc;
return a == other.a && b == other.b;
}
public override int GetHashCode()
{
return a.GetHashCode() ^ b.GetHashCode();
}
}
Hope it will help you..

containing type does not implement interface 'System.IComparable' error

namespace Theprogram.cs
{
class Program
{
static void Main(string[] args)
{
CreditCustomer[] creditCustomer = new CreditCustomer[5];
int x, y;
bool goodNum;
for (x = 0; x < creditCustomer.Length; ++x)
{
creditCustomer[x] = new CreditCustomer();
Console.Write("Enter customer number ");
creditCustomer[x].CustomerNumber = Convert.ToInt32(Console.ReadLine());
goodNum = true;
for (y = 0; y < x; ++y)
{
if (creditCustomer[x].Equals(creditCustomer[y]))
goodNum = false;
}
while (!goodNum)
{
Console.Write("Sorry, the customer number " +
creditCustomer[x].CustomerNumber + " is a duplicate. " +
"\nPlease reenter ");
creditCustomer[x].CustomerNumber = Convert.ToInt32(Console.ReadLine());
goodNum = true;
for (y = 0; y < x; ++y)
{
if (creditCustomer[x].Equals(creditCustomer[y]))
goodNum = false;
}
}
Console.Write("Enter name ");
creditCustomer[x].CustomerName = Console.ReadLine();
Console.Write("Enter age ");
creditCustomer[x].Rate = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter amount due ");
creditCustomer[x].AmountDue = Convert.ToDouble(Console.ReadLine());
}
Array.Sort(creditCustomer);
Array.Sort(customer);
}
}
class Customer : IComparable<Customer>
{
private int customerNumber;
public int CustomerNumber
{
get
{
return customerNumber;
}
set
{
customerNumber = value;
}
}
private string customerName;
public string CustomerName
{
get
{
return customerName;
}
set
{
customerName = value;
}
}
private double amountDue;
public double AmountDue
{
get
{
return amountDue;
}
set
{
amountDue = value;
}
}
public Customer(int num, string name, int amt)
{
CustomerNumber = num;
CustomerName = name;
AmountDue = amt;
}
public Customer(): this(9, "ZZZ", 0)
{
}
public override bool Equals(Object e)
{
bool equal;
Customer temp = (Customer)e;
if (this.CustomerNumber == temp.CustomerNumber)
equal = true;
else
equal = false;
return equal;
}
public override int GetHashCode()
{
return CustomerNumber;
}
public override string ToString()
{
return (GetType() + " Credit Customer " + CustomerNumber + " " + CustomerName +
" Amount Due is " + AmountDue.ToString("C") + " Interest rate is ");
}
protected virtual int IComparable.CompareTo(Object o)
{
int returnVal;
Customer temp = (Customer)o;
if (this.CustomerNumber >
temp.CustomerNumber)
returnVal = 1;
else
if (this.CustomerNumber < temp.CustomerNumber)
returnVal = -1;
else
returnVal = 0;
return returnVal;
}
}
class CreditCustomer : Customer, IComparable<CreditCustomer>
{
public int Rate {get; set;}
int MonthlyPayment { get; set; }
public CreditCustomer(int customerNumber, string customerName, int amountDue, int Rate) : base(customerNumber, customerName, amountDue)
{ }
public CreditCustomer(): this(0, "", 0, 0)
{ }
public override string ToString()
{
return (GetType() + " Credit Customer " + CustomerNumber + " " + CustomerName +
" Amount Due is " + AmountDue.ToString("C") + " Interest rate is " + Rate + " Monthly Payment is " + MonthlyPayment);
}
int IComparable.CompareTo(object o)
{
int returnVal;
CreditCustomer temp = (CreditCustomer)o;
if (this.CustomerNumber > temp.CustomerNumber)
returnVal = 1;
else
if (this.CustomerNumber < temp.CustomerNumber)
returnVal = -1;
else
returnVal = 0;
return returnVal;
}
}
}
I am having trouble implementing Icomparable in C# for my parent and child classes as I get the error:
does not implement interface member'System.IComparable.CompareTo
I really need help on how to implement the Icomparable interface so that I can use the compareTo method on my array of object. Any help would be greatly appreciated.
If you're using Visual Studio and are not sure how to implement an interface, just right click IComparable in the class declaration
class CreditCustomer : Customer, IComparable<CreditCustomer>
and select Implement Interface and any of the two options.
The correct signature for the implementation in CreditCustomer would be either:
public int CompareTo(CreditCustomer other)
{
}
or
int IComparable<CreditCustomer>.CompareTo(CreditCustomer other)
{
}
Note that the second signature would hide the method unless you cast the object to IComparable<CreditCustomer>.
As SLaks mentioned in his comment, there are, in fact, two different interfaces that are named IComparable which can be easily confused:
IComparable is an older, non-generic interface which allows comparison between an object and any other object. There's no type checking - your CreditCustomer can be compared to an int or a List<string> or anything. This interface defines a single method, CompareTo(object obj). This is the method you have in your class.
IComparable<T> is a newer, generic interface. It's very similar to IComparable, but enforces type checking - it only compares T to T, so you don't have to write a lot of boilerplate code making sure that someone didn't try to compare your CreditCustomer to a Dog or something. It also defines a single method - CompareTo(T obj). This is the interface that you marked your class as implementing.
Despite being similarly named, these two are, as far as the compiler is concerned, two different interfaces. If you don't have a CompareTo method that accepts a CreditCustomer argument, you're not implementing IComparable<CreditCustomer>, and that's why it's giving you an error. Either change your method signature to match the interface, or change the interface you're using to one that matches the method signature. Probably the former.

Covariant Collection not working

Sorry if the question is redundant, but I couldn't find a solution for my particular case.
Please consider this block of code:
public interface IPoint {}
public class GazePoint : IPoint {}
public Point AvgPoint(IEnumerable<IPoint> locations) {}
List<GazePoint> gazePoints = new List<GazePoint>();
//...
// this doesn't work:
Point avg = AvgPoint(gazePoints);
Could you please explain why it doesn't work (I was assuming C# 4.0 has solved this issue) and how can I change the signature of AvgPoint() method to make it possible to receive different implementations of IPoint. (I don't want to cast gazePoints collection to another type of collection, because it is in a big loop, and I'm concern about the performance.
[Update]: I had defined GazePoint as struct, and that was the source of problem. Still, I don't know why struct is not working here.
I'm not sure what the exact problem is you're having, but here's how it worked for me:
First, some actual class implementations:
public interface IPoint
{
int X { get; set; }
int Y { get; set; }
}
public class Point : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public Point()
{
}
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public class GazePoint : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public GazePoint()
{
}
public GazePoint(int x, int y)
{
X = x;
Y = y;
}
}
Then an actual AvgPoint method implementation:
public static Point AvgPoint(IEnumerable<IPoint> locations)
{
if (locations == null || !locations.Any()) return new Point(0, 0);
return new Point((int) locations.Average(l => l.X),
(int) locations.Average(l => l.Y));
}
And finally a few tests:
public static void Main()
{
var points = new List<Point>
{
new Point(1, 2),
new Point(3, 4)
};
var gazePoints = new List<GazePoint>
{
new GazePoint(1, 2),
new GazePoint(3, 4)
};
Point avgPoint = AvgPoint(points);
Point avgGazePoint = AvgPoint(gazePoints);
Console.WriteLine("Average Point = {0}, {1}", avgPoint.X, avgPoint.Y);
Console.WriteLine("Average GazePoint = {0}, {1}", avgGazePoint.X, avgGazePoint.Y);
}
If your goal is to have the method return the average in the same type that was passed in, you can make it generic like so:
public static T AvgPoint<T>(IEnumerable<T> locations) where T : IPoint, new()
{
if (locations == null || !locations.Any()) return new T {X = 0, Y = 0};
return new T {X = (int) locations.Average(l => l.X),
Y = (int) locations.Average(l => l.Y)};
}

How to define the operator ==

Given the class as follows,
public class Number
{
public int X { get; set; }
public int Y { get; set; }
}
How to define an overload operator == so that I can use the following statement:
Number n1 = new Number { X = 10, Y = 10 };
Number n2 = new Number { X = 100, Y = 100 };
if (n1 == n2)
Console.WriteLine("equal");
else
Console.WriteLine("not-equal");
// Updated based on comments as follows //
Here is a further question:
It seems to me that C# does operator overload differently from that of C++.
In C++, this overloaded operator is defined outside of the targeting class as a standalone function. Here in C#, this overload function in fact is embedded into the targeting class.
Can someone give me some comments on this topic?
Thank you
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
public class Number
{
public int X { get; set; }
public int Y { get; set; }
public Number() { }
public Number(int x, int y)
{
X = x;
Y = y;
}
public static bool operator==(Number a, Number b)
{
return ((a.X == b.X) && (a.Y == b.Y));
}
public static bool operator !=(Number a, Number b)
{
return !(a == b);
}
public override string ToString()
{
return string.Format("X: {0}; Y: {1}", X, Y);
}
public override bool Equals(object obj)
{
var objectToCompare = obj as Number;
if ( objectToCompare == null )
return false;
return this.ToString() == obj.ToString();
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
}
class Program
{
static void Main(string[] arg)
{
Number n1 = new Number { X = 10, Y = 10 };
Number n2 = new Number { X = 10, Y = 10 };
if (n1 == n2)
Console.WriteLine("equal");
else
Console.WriteLine("not-equal");
Console.ReadLine();
}
}
}
public static bool operator ==(YourClassType a, YourClassType b)
{
// do the comparison
}
More information here. In short:
Any type that overloads operator == should also overload operator !=
Any type that overloads operator == should also should override Equals
It is recommended that any class that overrides Equals also override GetHashCode
From the MSDN Docs:
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.x == b.x && a.y == b.y && a.z == b.z;
}
public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
return !(a == b);
}
Here is a short example of how to do it, but I very strongly recommend reading Guidelines for Overloading Equals() and Operator == (C# Programming Guide) to understand the interaction between Equals() and == and so on.
public class Number
{
public int X { get; set; }
public int Y { get; set; }
public static bool operator ==(Number a, Number b)
{
// TODO
}
}
overload the operator yourself:
public static bool operator ==(Number a, Number b)
{
// do stuff
return true/false;
}

Categories

Resources