HashSet overlaps does not work as expected - c#

Have the object model as shown below with HashSets:
When I try to find the matching set with the below statement does not work.
Could someone please suggest what am I missing here?
Would HashSet.CreateSetComparer solve the problem, if so could you please suggest how to use it?
HashSet Overlaps Statement:
var requirementSets = new HashSet<RequirementSet>();
// requirementSets has some data.
RequirementSet matchingSet = requirementSets.FirstOrDefault(e => e.RequirementOption s.Overlaps(lineItemRequirementSet.RequirementOptions));
Object Model:
public class RequirementSet : IEquatable<RequirementSet>
{
public ISet<RequirementOption> RequirementOptions { get; } = new HashSet<RequirementOption>();
public bool Equals(RequirementSet other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(RequirementOptions, other.RequirementOptions);
}
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((RequirementSet) obj);
}
public override int GetHashCode()
{
return RequirementOptions != null ? RequirementOptions.GetHashCode() : 0;
}
}
public class RequirementOption : IEquatable<RequirementOption>
{
public ISet<Requirement> Requirements { get; } = new HashSet<Requirement>();
public bool Equals(RequirementOption other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(Requirements, other.Requirements);
}
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((RequirementOption) obj);
}
public override int GetHashCode()
{
return Requirements != null ? Requirements.GetHashCode() : 0;
}
}
public class Requirement : IEquatable<Requirement>
{
public string Model {get; set;}
public bool Equals(Requirement other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ModelName == other.ModelName;
}
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((Requirement) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = ModelName != null ? ModelName.GetHashCode();
return hashCode;
}
}
}

Related

KeyNotFoundException : The given key was not present in the dictionary

I am receiving KeyNotFoundException whenever I try to access the key in dictionary.
I have a PricingRules class which has a dictionary:
public class PricingRules
{
Dictionary<ItemCode, Money> pricing_rules;
public PricingRules()
{
pricing_rules = new Dictionary<ItemCode, Money>();
pricing_rules.Add(new ItemCode("A"), new Money(50));
pricing_rules.Add(new ItemCode("B"), new Money(30));
pricing_rules.Add(new ItemCode("C"), new Money(20));
pricing_rules.Add(new ItemCode("D"), new Money(15));
}
public void itemScanned(ItemCode itemCode, Money amount)
{
amount.Add(pricing_rules[itemCode]);
}
public Money AmountForItem(ItemCode itemCode)
{
return pricing_rules[itemCode];
}
}
The key in the dictionary is of type ItemCode:
public class ItemCode
{
private readonly string itemCode;
public ItemCode(string itemCode)
{
this.itemCode = itemCode;
}
public override int GetHashCode()
{
return itemCode.GetHashCode();
}
}
and the value is of type Money:
public class Money
{
private int amount;
public Money(int amount)
{
this.amount = amount;
}
public override bool Equals(object obj)
{
Money money = (Money)obj;
return this.amount == money.amount;
}
public void Add(object obj)
{
Money money = (Money)obj;
this.amount += money.amount;
}
public override int GetHashCode()
{
return amount.GetHashCode();
}
}
I have tried overriding the GetHashCode() function to return the HashCode of the primitive type in the class but it still is giving KeyNotFoundException
The test I have written is:
public class PricingRulesShould
{
[Fact]
void ReturnValueFiftyWhenKeyIsA()
{
Money expected = new Money(50);
PricingRules pr = new PricingRules();
ItemCode itemcode = new ItemCode("A");
Money actual = pr.AmountForItem(itemcode);
Assert.Equal(expected, actual);
}
}
When Dictionary accesses its elements, it looks for the equality of keys.
Since the ItemCode is not a primitive type, when you compare new ItemCode("A") == new ItemCode("A") you should get false as a result, because they are different instances with identical contents.
What you can do is to implement IEquatable<ItemCode> by the ItemCode class:
public class ItemCode : IEquatable<ItemCode>
{
private readonly string itemCode;
public ItemCode(string itemCode)
{
this.itemCode = itemCode;
}
public override int GetHashCode()
{
return itemCode != null ? itemCode.GetHashCode() : 0;
}
public bool Equals(ItemCode other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(itemCode, other.itemCode);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is ItemCode ic && Equals(ic);
}
}

Abstract class to implement equality operators

Lets say I define the following abstract class:
public abstract class ValueEquality<T> : IEquatable<T>
where T : ValueEquality<T>
{
public override bool Equals(object obj)
{
return Equals(obj as T);
}
public static bool operator ==(ValueEquality<T> lhs, object rhs)
{
if (ReferenceEquals(lhs, rhs))
{
return true;
}
else if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
{
return false;
}
else
{
return lhs.Equals(rhs);
}
}
public static bool operator !=(ValueEquality<T> lhs, object rhs)
{
return !(lhs == rhs);
}
public bool Equals(T other)
{
return other != null && EqualNoNull(other);
}
public abstract override int GetHashCode();
public abstract bool EqualNoNull(T other);
}
And then create a class C as follows:
public class C : MyEquatable<C>
{
public override bool EqualsNoNull(C other)
{
...
}
public override int GetHashCode()
{
...
}
}
If I then have the code:
C x1;
C x2;
bool equal = x1 == x2;
Will this end up calling the equals method in C? Are there any gotchas with this approach?
Edit: fixed some issues in code raised by answers.
This code will do infinite loop in:
public override bool Equals(object obj)
{
try
{
T otherT = (T) obj;
return Equals(this, otherT);
}
catch (InvalidCastException)
{
return false;
}
}
It will call Equals(object obj) again and again. Right implementation:
public abstract class MyEquatable<T> : IEquatable<T>
where T : MyEquatable<T>
{
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 this.Equals((MyEquatable<T>)obj);
}
protected bool Equals(MyEquatable<T> other)
{
return this.Equals(other as T);
}
public static bool operator ==(MyEquatable<T> lhs, object rhs)
{
return Equals(lhs, rhs);
}
public static bool operator !=(MyEquatable<T> lhs, object rhs)
{
return Equals(lhs, rhs);
}
public abstract bool Equals(T other);
public abstract override int GetHashCode();
}
x1 == x2 will call operator == of MyEquatable, that will call Equals(object obj). Finally, it calls Equals(T other) overridden in C class
Another implementation which follows what is usually advised in documentation
public abstract class MyEquatable<T> : IEquatable<T>
where T : MyEquatable<T> {
public override bool Equals(object obj) {
if (ReferenceEquals(obj, null) || obj.GetType() != GetType())
return false;
var valueObject = obj as T; //Note the cast
if (ReferenceEquals(valueObject, null))
return false;
return Equals(valueObject); //Calls Equals(T other)
}
public abstract bool Equals(T other);
public abstract override int GetHashCode();
public static bool operator ==(MyEquatable<T> left, MyEquatable<T> right) {
if (ReferenceEquals(left, null) && ReferenceEquals(right, null))
return true;
if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
return false;
return left.Equals(right);
}
public static bool operator !=(MyEquatable<T> left, MyEquatable<T> right) {
return !(left == right);
}
}

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)

Implementing equality for 2d lines

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

Categories

Resources