Implementing equality for 2d lines - c#

I have simple class defining 2d line:
public class Line {
public double X1 { get; set; }
public double Y1 { get; set; }
public double X2 { get; set; }
public double Y2 { get; set; }
}
My primary goal is to get different lines from List using .Distinct(). In my case two lines are equal if theirs coordinates are equal regardless direction (line 1,2 -> 3,4 equals to 3,4 -> 1,2). I going to implement Equals like:
public override bool Equals(object obj) {
if (obj as Line == null) { return false; }
var second = (Line)obj;
if (this.X1 != second.X1 && this.X1 != second.X2) { return false; }
if (this.Y1 != second.Y1 && this.Y1 != second.Y2) { return false; }
if (this.X2 != second.X2 && this.X2 != second.X1) { return false; }
if (this.Y2 != second.Y2 && this.Y2 != second.Y1) { return false; }
return true;
}
but I have no idea how to implement GetHashCode (as I understand it's necessary to make it in case of using Distinct())

This becomes a bit easier if you first define a Point, then define your Line in terms of 2 Points.
Now, to calculate a reliable (but unaffected by direction) hash of a Line, make sure you order your points consistently when calculating the hash.
Putting this all together into a complete implementation (which also covers operator == and !=):
public class Point
{
public double X { get; set; }
public double Y { get; set; }
protected bool Equals(Point other)
{
return X.Equals(other.X) && Y.Equals(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.GetHashCode()*397) + Y.GetHashCode();
}
}
public static bool operator ==(Point left, Point right)
{
return Equals(left, right);
}
public static bool operator !=(Point left, Point right)
{
return !Equals(left, right);
}
}
public class Line
{
public Point Point1 { get; set; }
public Point Point2 { get; set; }
protected bool Equals(Line other)
{
return Equals(Point1, other.Point1) && Equals(Point2, other.Point2)
|| Equals(Point1, other.Point2) && Equals(Point2, other.Point1);
}
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((Line) obj);
}
public override int GetHashCode()
{
unchecked
{
var orderedPoints =
new[] {Point1, Point2}.OrderBy(p => p != null ? p.X : 0)
.ThenBy(p => p != null ? p.Y : 0).ToList();
var p1 = orderedPoints[0];
var p2 = orderedPoints[1];
return ((p1 != null ? p1.GetHashCode() : 0)*397)
+ (p2 != null ? p2.GetHashCode() : 0);
}
}
public static bool operator ==(Line left, Line right)
{
return Equals(left, right);
}
public static bool operator !=(Line left, Line right)
{
return !Equals(left, right);
}
}

Related

Possible null reference in IEquatable implementation

I'm trying to find out how to remove that possible null reference in the IEquatable implementation below.
return other != null && _guid == other._guid; Possible null reference argument for parameter 'left' in 'bool SubscriptionToken.operator !=(SubscriptionToken left, SubscriptionToken right)'
public class SubscriptionToken : IEquatable<SubscriptionToken>
{
public static readonly SubscriptionToken Empty = new(Guid.Empty);
private readonly Guid _guid;
private SubscriptionToken(Guid guid)
{
_guid = guid;
}
private SubscriptionToken()
{
}
public Guid Value => _guid;
public bool Equals(SubscriptionToken? other)
{
return other != null && _guid == other._guid; // Possible null reference
}
public bool IsValid()
{
return _guid != Guid.Empty;
}
public static SubscriptionToken New()
{
return new SubscriptionToken(Guid.NewGuid());
}
public override bool Equals(object? other)
{
return other is SubscriptionToken token && Equals(token);
}
public override int GetHashCode()
{
return HashCode.Combine(_guid);
}
public override string ToString()
{
return _guid.ToString("N");
}
public static bool operator ==(SubscriptionToken left, SubscriptionToken right)
{
return EqualityComparer<SubscriptionToken>.Default.Equals(left, right);
}
public static bool operator !=(SubscriptionToken left, SubscriptionToken right)
{
return !(left == right);
}
}
Your equals method should probably look something like this:
public bool Equals(SubscriptionToken other)
{
if (ReferenceEquals(null, other)) return false;
return _guid.Equals(other._guid);
}
That should avoid any null reference exceptions.

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

HashSet<T> EqualityComparer that returns equal if SetEquals: which GetHashCode? [duplicate]

I am trying to compare two hashsets of Definition type as EqualityComparer<T>.Default.Equals(value, oldValue). Definition is defined as follows
public class Definition
{
public string Variable { get; set; }
public HashSet<Location> LocationList { get; set; }
public override bool Equals(object obj)
{
Definition other = obj as Definition;
return other.Variable.Equals(this.Variable) && other.LocationList!= null &&this.LocationList != null
&& other.LocationList.Count == this.LocationList.Count
&& other.LocationList == this.LocationList;
}
public override int GetHashCode()
{
return this.Variable.GetHashCode() ^ this.LocationList.Count.GetHashCode();// ^ this.LocationList.GetHashCode();
}
}
public class Location
{
public int Line { get; set; }
public int Column { get; set; }
public int Position { get; set; }
public string CodeTab { get; set; }
public Location(int line, int col, int pos, string tab)
{
Line = line;
Column = col;
Position = pos;
CodeTab = tab;
}
public override bool Equals(object obj)
{
Location other = obj as Location;
return this.CodeTab == other.CodeTab
&& this.Position == other.Position
&& this.Column == other.Column
&& this.Line == other.Line;
}
public override int GetHashCode()
{
return this.CodeTab.GetHashCode() ^ this.Position.GetHashCode()
^ this.Column.GetHashCode() ^ this.Line.GetHashCode();
}
}
Somehow for a similar set, the result is returned as false despite all the information remaining the same. The only difference is that the position of some elements are interchanged, but I know that HashSet won't preserve or check the order while comparing. Can any one advise me on what is going wrong here?
PS: I tried uncommenting this.LocationList.GetHashCode() also, but didn't work.
You need to create a comparer for the sets:
var setComparer = HashSet<Location>.CreateSetComparer();
return other.Variable.Equals(this.Variable) && setComparer.Equals(this.LocationList, other.LocationList);
EqualityComparer<T>.Default will look for an object implementing IEquatable<T>. Otherwise, it will defer to an ObjectEqualityComparer, which simply checks for reference equality. This is why you're seeing false when references are compared.
What you actually want to do is explicitly implement IEquatable<Location>. Note you should really make your properties immutable for this to work properly:
public class Location : IEquatable<Location>
{
public Location(int line, int col, int pos, string tab)
{
Line = line;
Column = col;
Position = pos;
CodeTab = tab;
}
public int Line { get; private set; }
public int Column { get; private set; }
public int Position { get; private set; }
public string CodeTab { get; private set; }
public bool Equals(Location other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(CodeTab, other.CodeTab) && Column == other.Column && Line == other.Line && Position == other.Position;
}
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((Location) obj);
}
public static bool operator ==(Location left, Location right)
{
return Equals(left, right);
}
public static bool operator !=(Location left, Location right)
{
return !Equals(left, right);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (CodeTab != null ? CodeTab.GetHashCode() : 0);
hashCode = (hashCode*397) ^ Column;
hashCode = (hashCode*397) ^ Line;
hashCode = (hashCode*397) ^ Position;
return hashCode;
}
}
}
Now if you look at the type EqualityComparer created by Default, you'll see GenericEqualityComparer<Location>:
Console.WriteLine(EqualityComparer<Location>.Default.GetType())

C# - Stackoverflow exception when adding to HashSet<T>

I have got a class called Team.
class Team
{
public Team(string name)
{
this.Name = name;
this.Wins = 0;
this.Opponents = new HashSet<Team>();
}
public string Name { get; set; }
public int Wins { get; set; }
public HashSet<Team> Opponents { get; set; }
}
I get a Stackoverflow exception whenever i try to add an existing team with a single opponent in another team's HashSet that has zero Opponents guestTeam.Opponents.Add(homeTeam);
Here hometeam has a single opponents in Opponents while guestTeam.Opponents is still empty.
Its a small test app. Stacktrace's framecount shows 3.
Any ideas why would i get such an exception thrown?
I admit, I can't reproduce error. personally I could go into properly implement of
: IEquatable<Team>
And then:
public bool Equals(Team other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.Name, other.Name) && Equals(this.Opponents, other.Opponents);
}
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((Team)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((this.Name != null ? this.Name.GetHashCode() : 0) * 397) ^ (this.Opponents != null ? this.Opponents.GetHashCode() : 0);
}
}
public static bool operator ==(Team left, Team right)
{
return Equals(left, right);
}
public static bool operator !=(Team left, Team right)
{
return !Equals(left, right);
}
Of course I am cannot reproduce that so it's just shot.

C# Equality for classes having array properties

I have following value
public class Identification : IEquatable<Identification>
{
public int Id { get; set; }
public byte[] FileContent { get; set; }
public int ProjectId { get; set; }
}
Which I generated equality members for with resharper
public bool Equals(Identification other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id && Equals(FileContent, other.FileContent) && ProjectId == other.ProjectId;
}
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((Identification) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Id;
hashCode = (hashCode*397) ^ (FileContent != null ? FileContent.GetHashCode() : 0);
hashCode = (hashCode*397) ^ ProjectId;
return hashCode;
}
}
public static bool operator ==(Identification left, Identification right)
{
return Equals(left, right);
}
public static bool operator !=(Identification left, Identification right)
{
return !Equals(left, right);
}
But when I want to unit test it's equality before and after returning from the repository it fails. Despite having the exact same properties in the failure message.
var identification = fixture
.Build<Identification>()
.With(x => x.ProjectId, projet.Id)
.Create();
await repository.CreateIdentification(identification);
var returned = await repository.GetIdentification(identification.Id);
Assert.Equal() Failure
Expected: Identification { FileContent = [56, 192, 243], Id = 8, ProjectId = 42 }
Actual: Identification { FileContent = [56, 192, 243], Id = 8, ProjectId = 42 }
I'm using Npgsql with Dapper if it matters.
You should use Enumerable.SequenceEqual for arrays which checks for:
Both arrays are null or both arrays are not null.
Both arrays have same Length.
Corresponging items are equal to one another.
Something like this
public bool Equals(Identification other)
{
if (ReferenceEquals(null, other))
return false;
else if (ReferenceEquals(this, other))
return true;
return Id == other.Id &&
ProjectId == other.ProjectId &&
Enumerable.SequenceEqual(FileContent, other.FileContent);
}
Since Enumerable.SequenceEqual can well be time cosuming I've shifted it to the end of the comparison (there's no need to check arrays if, say, ProjectId are failed to be equal)

Categories

Resources