Possible null reference in IEquatable implementation - c#

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.

Related

How to enforce class types when comparing their int id properties

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>.

C# How to call overloaded operator in generic parent class?

Following problem:
In the definition of an function of a generic parent class ParentA I want to call an overloaded operator from ChildB:
See the following structure
class ParentA<T> where T : ParentB
{
public bool Contains(T element)
{
T element2;
return element==element2;
}
}
class ChildB : ParentB
{
public static bool operator ==(ChildB s1, ChildB s2)
{
//this one should be used
}
}
ChildA :ParentA<ChildB>
{
//make use of inherited function Contains(ChildB element)
}
I want to call the overloaded == operator of ChildB, but instead when I call the Contains function in ChildA the original ==operator from ParentB will be called.
Any idea how to make this work?
The Original code is a bit more nasty
public abstract class ElementBase
{
protected Element element;
public Element Element
{
get { return element; }
set { element = value; }
}
public static bool operator ==(ElementBase e1, ElementBase e2)
{
return e1.element == e2.element;
}
public static bool operator !=(ElementBase e1, ElementBase e2)
{
return e1.element != e2.element;
}
public override bool Equals(object obj)
{
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
return false;
else
{
ElementBase element = (ElementBase)obj;
return this == element;
}
}
}
public class Structure : ElementBase
{
public bool flag;
public static bool operator ==(Structure s1, Structure s2)
{
return s1.element == s2.element && s1.flag==s2.flag;
}
public static bool operator !=(Structure s1, Structure s2)
{
return s1.element != s2.element || s1.flag!=s2.flag;
}
public override bool Equals(object obj)
{
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
return false;
else
{
Structure structure = (Structure)obj;
return this == structure;
}
}
}
public class MapBase<T> where T : ElementBase
{
protected Dictionary<long, T> elements = new Dictionary<long, T>();
public bool Contains(T element)
{
...
return element==elements[id];
}
}
public class StructureMap : MapBase<Structure>
{
void someFunction()
{
//make use of Contains(Structure element)
}
}
Update
I replaced
return element==element2;
by
return element.Equals(element2);
and now it seems like it does the trick. I just don't understand why...

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

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())

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