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.
Related
I have several classes with id property of the same type int?:
public class Person {
public int? id { get; set; }
}
public class Project {
public int? id { get; set; }
}
// etc...
When writing code it happened that I compared semantically wrong types:
if (person.id == project.id), and of course there was no warning until I found the bug.
How could I create some kind of underlying type enforcement, or even better, a compiler warning, or something like that, that warns me not everything looks o.k.?
I can think of creating an Equals(Person p) { return p.id == this.id } but I'd prefer some other mechanism that could be used more 'freely'.
You need to override Equals and GetHashCode to be able to compare objects directly.
Try like this:
public sealed class Person : IEquatable<Person>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Person(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Person)
return Equals((Person)obj);
return false;
}
public bool Equals(Person obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Person left, Person right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Person left, Person right)
{
return !(left == right);
}
}
public sealed class Project : IEquatable<Project>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Project(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Project)
return Equals((Project)obj);
return false;
}
public bool Equals(Project obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Project left, Project right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Project left, Project right)
{
return !(left == right);
}
}
I also implemented IEquatable<Person> and == and != for good measure.
Now you can write person1 == this if this is a Person, but you would have a compiler error if this were a Project.
This is what tests are for. This is why you should write tests. Tests should pick up on these kind of errors.
But if you really want to go overkill, create a custom struct to store your IDs:
public struct Id<T> {
public int? ID { get; }
public static implicit operator Id<T>(int id) {
return new Id<T>(id);
}
public Id(int? id) { ID = id; }
public static bool operator ==(Id<T> lhs, Id<T> rhs) {
return lhs.ID == rhs.ID;
}
public static bool operator !=(Id<T> lhs, Id<T> rhs) {
return lhs.ID != rhs.ID;
}
}
// usage:
public class Person {
public Id<Person> Id { get; set; }
}
public class Project {
public Id<Project> Id { get; set; }
}
Whenever you try to compare Person.Id with Project.Id, the compiler will give you an error because you are comparing Id<Project> and Id<Person>.
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;
}
}
}
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())
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)
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);
}
}