"Equals" cannot be called in the overload operator function? - c#

I found an interesting thing, take this as a C# program:
namespace ConsoleApplication1
{
using System;
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public override bool Equals(object comparedStudent)
{
Student stu = comparedStudent as Student;
if (stu == null)
{
return false;
}
return ID.Equals(stu.ID) && Name.Equals(stu.Name);
}
public override int GetHashCode()
{
return ID.GetHashCode() ^ Name.GetHashCode();
}
/// <summary>
/// Notice that this will cause NullPointException……Why?
/// If I use "return s1.ID==s2.ID && s1.Name==s2.Name", that'd be OK.
/// </summary>
public static bool operator ==(Student s1, Student s2)
{
return s1.Equals(s2);
}
public static bool operator !=(Student s1, Student s2)
{
return !s1.Equals(s2);
}
}
public class Program
{
static void Main(string[] args)
{
Student s1 = new Student();
s1.ID = 1;
s1.Name = "YourName";
Student s2 = new Student();
s2.ID = 1;
s2.Name = "YourName";
//Why there's an exception here (NullPoint exception)
bool isSame = (s1 == s2);
Console.WriteLine(isSame);
}
}
}
If you copy my codes and run, a NullPointer exception will be thrown out. Why?
PS:If I use "return s1.ID==s2.ID && s1.Name==s2.Name" in the overload operator +, that'd be OK.
Why? I'm using net4.0 VS2015.
And if you debug by dropping the debug point at "bool isSame = (s1 == s2);" You'll soon find it enters into the "==" overload function 3 times, and in the end, s1 and s2 are null!

Although Ians comment will fix the above issue it will not finally fix the problem in general.
The origin of the problem is in the overload of the comparison operators.
A programmer expects null == myobject as well as myobject == null to be a valid expression (as you did as well in implementation of .Equals). This does not work for the above class because of the invocation of the member function s1.Equals in operator ==.
So any reasonable overload of a comparison operator should be able to handle really any input without throwing an exception. In doubt simply return false.
This includes null values as well as class instances that do not yet have all properties assigned.
public static bool operator ==(Student s1, Student s2) =>
ReferenceEquals(s1, s2) || (!ReferenceEquals(s1, null) && s1.Equals(s2));
Rationale:
ReferenceEquals(s1, s2) handles null == null as well as x == x. The latter does not require to compare the content.
s1 != null handles null == x.
x == null is already handled by the .Equals implementation.
By the way: it is recommended to provide a strongly typed .Equals implementation when overloading equality operators by implementing IEquatable<Student>.
Furthermore you should not use .Equals to compare components, because this will sadly fail for a similar reason if Name has not been assigned so far.
public override bool Equals(object obj) =>
Equals(obj as Student);
public bool Equals(Student stu) =>
!ReferenceEquals(stu, null) && ID == stu.ID && Name == stu.Name;
See also the reply of Yuval Itzchakov to question Operator overloading ==, !=, Equals

Related

c# List contain Item

I have a class named accessoire :
class accessoire
{
public int value1 { get; set; }
public string Value2 { get; set; }
}
then i have a List of that product
List<accessoire> accessoires
And i have an UI where the user pick the product he wants from a DataGridview and when he selected it launch an event that add this item to the list :
private void ProductBrowser_OnItemAdded(Accessoire item)
{
if (Cart.Contains(item))
{
MessageBox.Show("Produit deja ajoutée au panier ! ");
}
else
{
Cart.Add(item);
ProductView.Rows.Add(item.Ref, item.Name, Function.CatName(item.Cat), item.SellPrice, "1", Convert.ToDouble(item.SellPrice) * Convert.ToDouble(item.QtetoSell));
TotalPriceSet();
MessageBox.Show("Produit Ajouté !");
}
}
this doesnt work , but when i change it to :
private void ProductBrowser_OnItemAdded(Accessoire item)
{
var InList = Cart.Find(product => product.Ref == item.Ref);
if (Cart.Contains(InList))
{
MessageBox.Show("Product already in list ! ");
}
else
{
Cart.Add(item);
ProductView.Rows.Add(item.Ref, item.Name, Function.CatName(item.Cat), item.SellPrice, "1", Convert.ToDouble(item.SellPrice) * Convert.ToDouble(item.QtetoSell));
TotalPriceSet();
MessageBox.Show("product added !");
}
}
it works , but i'am still wondering why the first code doesnt work it keep adding that item to the list ? in other way how does the method .Contains()works ? what does it check to know if the item is or the list on not ?
The reason it doesn't find the object in the list is because it is a reference comparison, comparing the instances of the object, not the values. You can have two instances of your class with the same values in their properties, but if you compare them, they are not equal:
accessoire item1 = new accessoire();
item1.value1 = 1;
item1.value2 = "one";
accessoire item2 = new accessoire();
item2.value1 = 1;
item2.value2 = "one";
if(item1 == item2) MessageBox.Show("Same");
else MessageBox.Show("Different");
When you select the item from the list to compare with, you are pulling the specific instance, which does exist in the list.
You need to implement the Equals method of accessoire to properly compare all properties/fields in it. The default implementation of Equals uses ReferenceEquals, which only works if the two instances are in fact the same.
if (Cart.Contains(item))
is matching by equality.
If object.Equals(T) is not satisified, it will fail. That means the smallest change, even whitespace in a string, will return false. You'll also get a false result if you have two instances of the same class. Contains must refer to exactly item.
var InList = Cart.Find(product => product.Ref == item.Ref) is a match by property. This means that other properties of the product/item can all be different, as long as .Ref matches. I presume Ref is a primary key, which is why you're not getting problems in your result where Find() returns the wrong item.
You can get around the difference by overriding Equals for Cart, but I don't recommend it. It can make debugging hell later.
Just implement the equals method
// override object.Equals
public override bool Equals(object obj)
{
//
// See the full list of guidelines at
// http://go.microsoft.com/fwlink/?LinkID=85237
// and also the guidance for operator== at
// http://go.microsoft.com/fwlink/?LinkId=85238
//
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var data = (accessoire)obj;
return this.Ref.Equals(data.Ref);
}
// override object.GetHashCode
public override int GetHashCode()
{
return this.Ref.GetHashCode()
}
You were doing reference compare and the references don't match in your first example, but do in your second. You probably want to do equality comparison. Are the values of both objects the same.
Below is your class implemented with the various methods used for equality comparing. You would just need to modify them to suit your purposes.
// IEquatable<T> provides typed equality comparing
class accessoire : IEquatable<accessoire>
{
public int Value1 { get; set; }
public string Value2 { get; set; }
// you can override Equals.
public override bool Equals(object obj)
{
return this.Equals(obj as accessoire);
}
// this comes from IEquatable<T>
public bool Equals(accessoire other)
{
if (ReferenceEquals(null, other))
{
return false;
}
// return the comparison that makes them equal.
return
this.Value1.Equals(this.Value1) &&
this.Value2.Equals(this.Value2);
}
public override int GetHashCode()
{
unchecked
{
int hash = 37;
hash *= 23 + this.Value1.GetHashCode();
hash *= 23 + this.Value2.GetHashCode();
return hash;
}
}
// allows you to check equality with the == operator
public static bool operator ==(accessoire left, accessoire right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if ((oject)left == null || (object)right == null)
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(accessoire left, accessoire right)
{
return !left.Equals(right);
}
}

Equating derived classes based on their base's Equals()

I have two classes which both derive from the same parent:
public class People{
public string BetterFoot;
public override bool Equals(object obj){
if (obj == null || this.GetType() != obj.GetType())
return false;
People o = (People)obj;
return (this.BetterFoot == o.BetterFoot);
}
public class LeftiesOrRighties: People{
public string BetterHand;
public override bool Equals(object obj){
if (obj == null || this.GetType() != obj.GetType())
return false;
LeftiesOrRighties o = (LeftiesOrRighties)obj;
return (this.BetterFoot == o.BetterFoot) &&
(this.BetterHand == o.BetterHand)
}
}
public class Ambidextrous: People{
public string FavoriteHand;
}
(There are GetHashCodes in there, too, but I know that they work.)
I'd like to compare collections of them, based on their root Equals():
ThoseOneHanded = new List<LeftiesOrRighties>(){new LeftiesOrRighties(){BetterFoot = "L"}};
ThoseTwoHanded = new List<Ambidextrous>(){new Ambidextrous(){BetterFoot = "L"}};
//using NUnit
Assert.That ((People)ThoseOneHanded[0], Is.EqualTo((People)ThoseTwoHanded[0])));
Unfortunately, this returns false.
Why? Shouldn't the casting make them (for all intents and purposes, if not exactly) the same type, and thus use the base methods? And if not, how do I truly cast the underlying type back to People?
Cast doesn't change the object itself, so the result of GetType will always be the same and so your this.GetType() != obj.GetType() will be true and so the function will return false.
The following logic could potentially gain the behaviour you want (and you don't need to cast to People)
public class People
{
public string BetterFoot;
public override bool Equals(object obj)
{
var o = obj as People;
if (o == null) return false;
return (this.BetterFoot = o.BetterFoot);
}
public class LeftiesOrRighties: People
{
public string BetterHand;
public override bool Equals(object obj)
{
var o = obj as LeftiesOrRighties;
if ( o == null) return base.Equals(obj);
return (this.BetterFoot = o.BetterFoot) && (this.BetterHand = o.BetterHand)
}
}
public class Ambidextrous: People
{
public string FavoriteHand;
}
As Bob Vale pointed out, the cast does not change type.
The standard solution used across the .NET Framework is to use custom object implementing IEqualityComparer or its generic variant. Then your compare/find method takes 2 objects/collections and uses comparer to perform the custom comparison.
I.e. many LINQ methods take custom compare to find/filter objects like
Enumerable.Distinct
public static IEnumerable<TSource> Distinct<TSource>(
this IEnumerable<TSource> source,
IEqualityComparer<TSource> comparer
)
Sample comparer:
class Last3BitsComparer : IEqualityComparer<int>
{
public bool Equals(int b1, int b2)
{
return (b1 & 3) == (b2 & 3);
}
public int GetHashCode(int bx)
{
return bx & 3;
}
}

((System.Object)p == null)

Why do this:
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
Instead of this:
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if (p == null)
{
return false;
}
I don't understand why you'd ever write ((System.Object)p)?
Regards,
Dan
You cast to object when you don't know or can't be sure whether the original class has overridden operator ==:
using System;
class AlwaysEqual
{
public static bool operator ==(AlwaysEqual a, AlwaysEqual b)
{
return true;
}
public static bool operator !=(AlwaysEqual a, AlwaysEqual b)
{
return true;
}
}
class Program
{
static void Main()
{
object o = new AlwaysEqual();
AlwaysEqual ae = o as AlwaysEqual;
if (ae == null)
{
Console.WriteLine("ae is null");
}
if ((object)ae == null)
{
Console.WriteLine("(object)ae is null");
}
}
}
This code outputs only "ae is null", which is obviously not the case. The cast to object avoids the AlwaysEqual class's operator == and is therefore a true reference check against null.
Every object in .NET is derived from System.Object so there is no need for explicit cast.
And even more concise would be:
if (!(obj is TwoDPoint)) {
return false;
}
Plainly speaking, It's pointless. Null can always be assigned(except for non-nullables such as ints and structs) regardless of type so it can always be checked for. The cast is not necessary
If TwoDPoint is a non-nullable type such as a struct then indeed it may have a point. The (System.Object) cache would effectively box the struct into a nullable object. But if that was the case then obj as TwoDPoint would not be valid. You would need obj as TwoDPoint? to make it nullable. (can't use as with non-nullables)
It makes total sense if that code is inside Object.Equals override and you don't want to invoke the equality operator (which might, for example, erroneously call Equals). Casting to object allows to call standard equality operator, which compares references.
Normally, you would use Object.ReferenceEquals to compare an instance of an object to null inside Equals override.
For example, this would cause a stack overflow:
public class Point {
public override bool Equals (object other) {
var otherPoint = other as Point;
if (other == null)
return false;
//...
}
public static bool operator == (Point l, Point r) {
//...
//null checks
if (!l.Equals(r))
return false;
}
}
In the above example equality operator calls Equals and because otherPoint variable is of type Point, it would invoke the equality operator, causing infinite recursion.
Normally, when you override Equals and define the equality operator, you would put the comparison logic in the operator and invoke that from the Equals override. Bear in mind that it's recommended for the class to be immutable if both are overridden.
public class Point {
public override bool Equals (object other) {
var otherPoint = other as Point;
return this == otherPoint;
}
//must override GetHashCode() as well
public static bool operator == (Point l, Point r) {
if (Object.ReferenceEquals(l, null) && Object.ReferenceEquals(r, null))
return true;
if (Object.ReferenceEquals(l, null) || Object.ReferenceEquals(r, null))
return false;
//actual equality checks
}
public static bool operator != (Point l, Point r) {
return !(l==r);
}
}

How do I override the equals operator == for an interface in C#?

I have defined the following interface:
public interface IHaveAProblem
{
string Issue { get; set; }
}
And here is the implementation of IHaveAProblem:
public class SomeProblem : IHaveAProblem
{
public string Issue { get; set; }
public override bool Equals(object obj)
{
SomeProblem otherObj = obj as SomeProblem;
if (otherObj == null)
{
return false;
}
return this.Issue == otherObj.Issue;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool operator ==(SomeProblem rhs, SomeProblem lhs)
{
// Null check
if (Object.ReferenceEquals(rhs, null) || Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null) && Object.ReferenceEquals(lhs, null))
{
// Both are null. They do equal each other
return true;
}
// Only 1 is null the other is not so they do not equal
return false;
}
return rhs.Equals(lhs);
}
public static bool operator !=(SomeProblem rhs, SomeProblem lhs)
{
// Null check
if (Object.ReferenceEquals(rhs, null) || Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null) && Object.ReferenceEquals(lhs, null))
{
// Both are null. They do equal each other
return false;
}
// Only 1 is null the other is not so they do not equal
return true;
}
return !rhs.Equals(lhs);
}
}
When I use the object, I can get the correct results for the == compare:
SomeProblem firstTest = new SomeProblem()
{
Issue = "Hello World"
};
SomeProblem secondTest = new SomeProblem()
{
Issue = "Hello World"
};
// This is true
bool result = firstTest == secondTest;
However, when I try to compare the interfaces, it is doing a memory compare rather than the operator == on SomeProblem:
IHaveAProblem firstProblem = new SomeProblem()
{
Issue = "Hello World"
};
IHaveAProblem secondProblem = new SomeProblem()
{
Issue = "Hello World"
};
Is it possible to have the interface use the == on SomeProblem rather than a memory compare?
I know I can do a firstProblem.Equals(secondProblem) and get the proper results. However, I am creating a framework and I will not know how it is used in the end. I thought == would work correctly.
The operator == is static. You cannot define static methods for interfaces in C#. Also, for all operators at least one of the argument types needs to be of the same type as the class it is defined in, therefore: No operator overloading for interfaces :(
What you CAN do is use an abstract class instead - and define the operator there. Again, the operator may NOT be virtual (since static methods cannot be virtual...)
[Edited, reason see comment.]
I konw, this is an old question, but all examples provided show how to compare two class instances, and no one points out how to compare two interface instances.
In some cases, this is the DRYest way to compare interfaces.
public interface IHaveAProblem
{
string Issue { get; set; }
}
public class IHaveAProblemComparer : IComparer<IHaveAProblem>, IEqualityComparer<IHaveAProblem>
{
public int Compare(IHaveAProblem x, IHaveAProblem y)
{
return string.Compare(x.Issue, y.Issue);
}
public bool Equals(IHaveAProblem x, IHaveAProblem y)
{
return string.Equals(x.Issue, y.Issue);
}
public int GetHashCode(IHaveAProblem obj)
{
return obj.GetHashCode();
}
}
Usage?
IHaveAProblemComparer comparer = new IHaveAProblemComparer();
List<IHaveAProblem> myListOfInterfaces = GetSomeIHaveAProblemObjects();
myListOfInterfaces.Sort(comparer); // items ordered by Issue
IHaveAProblem obj1 = new SomeProblemTypeA() { Issue = "Example1" };
IHaveAProblem obj2 = new SomeProblemTypeB() { Issue = "Example2" };
bool areEquals = comparer.Equals(obj1, obj2); // False
IIRC (and I could be wrong here), C# interfaces don't allow operator overloading.
But in this case that's okay. The == operator normally maps to reference equality. It sounds like you want value equality, and that means you want to force them to override the .Equals() (and consequently also .GetHashCode()) functions. You do that by having your interface inherit from IEquatable.
Have you tried implementing IComparable?
Like this:
public interface IHaveAProblem : IComparable
{
string Issue { get; set; }
}
And then in the implementation of the class:
public class SomeProblem : IHaveAProblem
{
public string Issue { get; set; }
...
public int CompareTo(object obj)
{
return Issue.CompareTo(((SomeProblem)obj).Issue);
}
}
Note that, this works only when you compare two instances of SomeProblem, but not any other implementations of the IHaveAProblem interface.
Not sure if there could occur a NullReferenceException.

Overriding Equals and comparing to string

I've defined a C# class with a string member. For all intents an purposes, think of this class as being a subclass of string (except that's not allowed). I'm using it to represent a strongly typed string field that matches a specific format (I've simplified this significantly).
public class field
{
private readonly string m_field;
public field(string init_value)
{
//Check the syntax for errors
if (CheckSyntax(init_value))
{
m_field = init_value;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
public override string ToString()
{
return m_field;
}
}
Now, I want to be able to compare this class directly to any other string (object or literal). Therefore, I implemented the following in the class:
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
return this.m_field == obj.ToString();
}
public override int GetHashCode()
{
return this.m_field.GetHashCode();
}
public static bool operator ==(field x, Object y)
{
if ((object)x == null && y == null)
{
return true;
}
else if ((object)x == null || y == null)
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}
public static bool operator !=(field x, Object y)
{
return !(x == y);
}
Now when I'm writing a unit test, depending on the order that I'm passing in the arguments to Assert.AreEqual, I get different results:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // PASSES
Assert.AreEqual(valid, target); // FAILS
I'm assuming this is because in the first assert, it's calling field.Equals() and in the second it's calling String.Equals(). Obviously I'm approaching this from the wrong angle. Can anyone give me some insight?
One other thing. I can't use a struct here (value type) because in my actual case I'm defining all this in a base class and inheriting from it.
Basically you can't do what you want to - there's no way you can make string recognise your class for equality purposes. You'll never be able to make it reflexive - you'll never be able to make it obey the contract of object.Equals.
I would personally try to redesign it so that you didn't have the validation as part of the type itself - make it part of the relevant properties of the business entities (or whatever they are).
This is described in detail in Effective Java as Item 8: Obey the general contract when overriding equals.
The equals method implements an equivalence relation.
It is Reflexive, Symmetric, Transitive, Consistent, and for any non-null reference x, x.equals(null) must return false. The example cited to break symmetry is similar to yours.
field class is aware of string class, but the built-in string class is not aware of field. This a one-way interoperability, and should be removed.
I would discourage anyone using your field class implicitly as a String, and force this type of usage:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid);
Assert.AreEqual(valid, target.toString());
Based on everyone's feedback, and my own needs, here's what I'm putting forward as a possible solution (I'm modifying the Equals method as follows):
public override bool Equals(Object obj)
{
if (obj == null)
{
return false;
}
field f = obj as field;
if (f != null)
{
return this == f;
}
else
{
return obj.Equals(this);
}
}
This seems to allow its correct use in dictionary and collection classes that rely on the Equals and GetHashCode methods for determining if the value already exists.
Also, now these both fail:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // FAILS
Assert.AreEqual(valid, target); // FAILS
And these both pass:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES
And these both pass:
field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES
This is String#Equals
public override bool Equals(object obj)
{
string strB = obj as string;
if ((strB == null) && (this != null))
{
return false;
}
return EqualsHelper(this, strB);
}
Supplying an argument other than a String to String#Equals is going to return false. I'd suggest a 'rethink' to get around this.
I suggest using object.ReferenceEquals() if you are internally trying to validate whether x or y is null.
public static bool operator ==(field x, Object y)
{
if (object.ReferenceEquals(x, null) && object.ReferenceEquals(y, null))
{
return true;
}
else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}

Categories

Resources