Object.Equals clarification in C#? - c#

I've made a simple test :
object t = 3;
object aa = 3;
#1 Console.WriteLine(t.Equals(aa));
#2 Console.WriteLine(t.Equals(3));
#3 Console.WriteLine(3.Equals(aa));
All of them are true.(that's my problem actually).
looking at object , this is the used function:
public virtual bool Equals(object obj);
The equals is virtual. so it can be overridden.
But I don't see any polymorphic behavior. this is just a pure boxed value.
Regarding line #1 t.Equals(aa)
The reference type is the static type - Object.
so I thought it should call Object.Equals : which means that the reference are different , meaning the first answer should be False.(and I probably wrong here). why is that?
Regarding line #2 t.Equals(3)
Again, t's static type is object. so Object.Equals is running. how come it is true ?
Regarding line #3 3.Equals(aa)
I believe it is the public override bool Equals(object obj); is running because the static type is int. and the param type is object. but why does it true ? does it un-box the value ?
it seems that something , somehow unboxes the object without my notice :-(

Objects Equals method is polymorphic, so it can be overriden by subtypes like int. Int32.Equals overrides this method to do a value comparison between the current object and its argument, and since the arguments are equal once unboxed, it returns true.
There are two overload of Int32.Equals - bool Equals(object) and bool Equals(int). The bool Equals(object) overload is the one overriden from object. Since t and aa are object references, this is the method which will be called in examples 1 and 2.
In example 3, it is still this overload which is called since aa is an object and this is therefore the only valid overload.
The == operator is static and is resolved statically based on the types of its arguments, which are both object in your example. The == operator for object compares references, and in this case will return false for two separate boxed ints.

The virtual method Object.Equals is being called, but because of the way virtual methods work, it calls the Int32.Equals method instead, which compares the int values, not the references.
Virtual methods are bound at runtime. That is, they choose the appropriate method at runtime, not at compile time. In this case, Object.Equals is what is in the compiled code, but since you're comparing ints, it chooses Int32.Equals at runtime. This is accomplished using something called v-tables (in case you wanted to read more on this).
Keep in mind that Equals is supposed to act like this, and if you really want reference equality, you can use ReferenceEquals.
Note that this doesn't have anything to do with boxing. You'll get the same behaviour with, for example, a string, or a custom class.

As you wrote in your question the following Asserts will all pass
[Test]
public void EqualityOnInts()
{
object a = 1;
object b = 1;
Assert.AreEqual(a, b);
Assert.IsTrue(1.Equals(a));
Assert.IsTrue(b.Equals(1));
}
If you instantiate a you create a new integer object with value 1. Calling the Equals method on a will result in calling the Equals method on Int32. Also, if you do a.GetType() it will returnInt32`.
Since the Equals implementation of Int32 will check if the value is equal, and does not care about a different object reference, the result will be "true".

Related

IFormattable Equals method does not have correct behaviour

On C# I'm having this issue as presented it the image. Does anyone knows why the Equals implementation is not giving me the expected behaviour? Wasn't supposed to return true because the value is the same?
Since FormattableString doesn't override .Equals and neither does ConcreteFormattableString which inherits from FormattableString, it reverts to Object.Equals which compares references for equality, that is why your current code returns false.
A quick fix would be to call .ToString() on both objects and use the string.Equals override as it compares the value of each string.
So you could switch your last line to this and it should produce true:
Console.WriteLine(string.Equals(x.ToString(), u.ToString()));
Documentation:
string.Equals(): https://learn.microsoft.com/en-us/dotnet/api/system.string.equals?view=netframework-4.7.2
FormattableString: https://learn.microsoft.com/en-us/dotnet/api/system.formattablestring?view=netframework-4.7.2
ConcreteFormattableString: https://weblogs.asp.net/dixin/csharp-6-0-string-interpolation-formattablestring-and-code-analysis-ca1305-specify-iformatprovider
The type of your variables are ConcreteFormattableString because of this assignment:
IFormattable x = $"string1"; // ConcreteFormattableString
var x = $"string1"; // this is regular string
And ConcreteFormattableString doesn't override Equals method, so object.Equals method is called which compares references. Since the two string have different references, you get false.
As mentioned in the comments if you want to compare contents of the strings, you can call ToString, then perform the comparison.

C# object{int} not equal to object{int} [duplicate]

Today I stumbled upon an interesting bug I wrote. I have a set of properties which can be set through a general setter. These properties can be value types or reference types.
public void SetValue( TEnum property, object value )
{
if ( _properties[ property ] != value )
{
// Only come here when the new value is different.
}
}
When writing a unit test for this method I found out the condition is always true for value types. It didn't take me long to figure out this is due to boxing/unboxing. It didn't take me long either to adjust the code to the following:
public void SetValue( TEnum property, object value )
{
if ( !_properties[ property ].Equals( value ) )
{
// Only come here when the new value is different.
}
}
The thing is I'm not entirely satisfied with this solution. I'd like to keep a simple reference comparison, unless the value is boxed.
The current solution I am thinking of is only calling Equals() for boxed values. Doing a check for a boxed values seems a bit overkill. Isn't there an easier way?
If you need different behaviour when you're dealing with a value-type then you're obviously going to need to perform some kind of test. You don't need an explicit check for boxed value-types, since all value-types will be boxed** due to the parameter being typed as object.
This code should meet your stated criteria: If value is a (boxed) value-type then call the polymorphic Equals method, otherwise use == to test for reference equality.
public void SetValue(TEnum property, object value)
{
bool equal = ((value != null) && value.GetType().IsValueType)
? value.Equals(_properties[property])
: (value == _properties[property]);
if (!equal)
{
// Only come here when the new value is different.
}
}
( ** And, yes, I know that Nullable<T> is a value-type with its own special rules relating to boxing and unboxing, but that's pretty much irrelevant here.)
Equals() is generally the preferred approach.
The default implementation of .Equals() does a simple reference comparison for reference types, so in most cases that's what you'll be getting. Equals() might have been overridden to provide some other behavior, but if someone has overridden .Equals() in a class it's because they want to change the equality semantics for that type, and it's better to let that happen if you don't have a compelling reason not to. Bypassing it by using == can lead to confusion when your class sees two things as different when every other class agrees that they're the same.
Since the input parameter's type is object, you will always get a boxed value inside the method's context.
I think your only chance is to change the method's signature and to write different overloads.
How about this:
if(object.ReferenceEquals(first, second)) { return; }
if(first.Equals(second)) { return; }
// they must differ, right?
Update
I realized this doesn't work as expected for a certain case:
For value types, ReferenceEquals returns false so we fall back to Equals, which behaves as expected.
For reference types where ReferenceEquals returns true, we consider them "same" as expected.
For reference types where ReferenceEquals returns false and Equals returns false, we consider them "different" as expected.
For reference types where ReferenceEquals returns false and Equals returns true, we consider them "same" even though we want "different"
So the lesson is "don't get clever"
I suppose
I'd like to keep a simple reference comparison, unless the value is boxed.
is somewhat equivalent to
If the value is boxed, I'll do a non-"simple reference comparison".
This means the first thing you'll need to do is to check whether the value is boxed or not.
If there exists a method to check whether an object is a boxed value type or not, it should be at least as complex as that "overkill" method you provided the link to unless that is not the simplest way. Nonetheless, there should be a "simplest way" to determine if an object is a boxed value type or not. It's unlikely that this "simplest way" is simpler than simply using the object Equals() method, but I've bookmarked this question to find out just in case.
(not sure if I was logical)

Why the test 'Assert.AreEqual' has failed when I compare two empty list?

I have a class MyCustomClass:
public MyCustomClass
{
public MyCustomClass()
{
MyObject = new List<MyCustomObject>();
}
public List<MyCustomObject> MyObject {get; set;}
}
In the Test:
List<MyCustomObject> aux = new List<MyCustomObject>();
MyCustomClass oClass = new MyCustomClass();
Assert.AreEqual(aux, oClass.MyObject)
The test has failed, why? Every property, static member, etc are the same.
In this case Assert.AreEqual will check to see if the two objects are the same, and they're not. You should use CollectionAssert.AreEqual instead, which will return true if the two "have the same elements in the same order and quantity."
As already answered, two lists of same type with zero elements are not considered equal.
The reason behind this, is that AreEqual actually calls aux.AreEqual(oClass.MyObject), using the objects own equality implementation. Because this is not overridden for List<T>, it falls back to the implementation in Object, which is a simple reference equality check. Those two lists are clearly not the same reference, and therefore, they are not considered equal.
Because the Equals method exists and is virtual on Object, your own classes can override Equals in order to provide another concept for equality than reference equality. This is done on objects like String, which compare equal even for different references, if the data is the same.
I decompiled Assert.AreEqual (which is in the Microsoft.VisualStudio.QualityTools.UnitTestFramework GAC assembly) using dotPeek and found that Assert.AreEqual(aux, oClass.MyObject) will ultimately result in the following call where aux is expected and oClass.MyObject is actual:
object.Equals((object) expected, (object) actual)
From the documentation for the static object.Equals(Object, Object) we read:
The static Equals(Object, Object) method indicates whether two
objects, objA and objB, are equal. It also enables you to test objects
whose value is null for equality. It compares objA and objB for
equality as follows:
It determines whether the two objects represent the same object
reference. If they do, the method returns true. This test is
equivalent to calling the ReferenceEquals method. In addition, if both
objA and objB are null, the method returns true.
It determines whether either objA or objB is null. If so, it returns
false.
If the two objects do not represent the same object reference and
neither is null, it calls objA.Equals(objB) and returns the result.
This means that if objA overrides the Object.Equals(Object) method,
this override is called.
Now, List<T> is known to be a reference type, and we know that neither of the two lists you are comparing are null, so the final comparison between your two objects will be
expected.Equals(actual)
Since List<T> does not override Equals, it uses the base object implementation, which performs reference comparison, and thus fails (expected and actual were "newed" separately).
What you want is structural comparison, i.e. the pair-wise equality of the elements in your lists. See #ReedCopsey answer for the correct assertion for that (CollectionAssert.AreEqual).

Whats the difference between these two comparison statements?

Whats the difference between these two comparison statments?
var result = EqualityComparer<T>.Default.Equals(#this, null);
var result = #this == null;
Obviously the aim is to test whether the object '#this' isnull.
Well it depends on the type of #this. If it doesn't have an overload of ==, the second line will just perform a direct reference comparison, whereas the first line will call an overridden Equals method or an implementation of IEquatable.Equals.
Any sensible implementation will give the same result for both comparisons.
The first statement calls the Equals() method between objects to see if their values are equal, assuming it has been overriden and implemented in the class T. The second statement compares the references instead, unless the == operator has been overridden like in the String class.
operator == calls ReferenceEquals on comparing objects, so compare that objects are pointing to the same memory location.
Equals, instead, is a just virtual method, so can behave differently for different types, as it can be overriden.
For example, for CLR string Equals compares content of a string and not a reference, even if string is a reference type.

When can a == b be false and a.Equals(b) true?

I ran into this situation today. I have an object which I'm testing for equality; the Create() method returns a subclass implementation of MyObject.
MyObject a = MyObject.Create();
MyObject b = MyObject.Create();
a == b; // is false
a.Equals(b); // is true
Note I have also over-ridden Equals() in the subclass implementation, which does a very basic check to see whether or not the passed-in object is null and is of the subclass's type. If both those conditions are met, the objects are deemed to be equal.
The other slightly odd thing is that my unit test suite does some tests similar to
Assert.AreEqual(MyObject.Create(), MyObject.Create()); // Green bar
and the expected result is observed. Therefore I guess that NUnit uses a.Equals(b) under the covers, rather than a == b as I had assumed.
Side note: I program in a mixture of .NET and Java, so I might be mixing up my expectations/assumptions here. I thought, however, that a == b worked more consistently in .NET than it did in Java where you often have to use equals() to test equality.
UPDATE Here's the implementation of Equals(), as requested:
public override bool Equals(object obj) {
return obj != null && obj is MyObjectSubclass;
}
The key difference between == and Equals is that == (like all operators) is not polymorphic, while Equals (like any virtual function) is.
By default, reference types will get identical results for == and Equals, because they both compare references. It's also certainly possible to code your operator logic and Equals logic entirely differently, though that seems nonsensical to do. The biggest gotcha comes when using the == (or any) operator at a higher level than the desired logic is declared (in other words, referencing the object as a parent class that either doesn't explicitly define the operator or defines it differently than the true class). In such cases the logic for the class that it's referenced as is used for operators, but the logic for Equals comes from whatever class the object actually is.
I want to state emphatically that, based solely upon the information in your question, there is absolutely no reason to think or assume that Equals compares values versus references. It's trivially easy to create such a class, but this is not a language specification.
Post-question-edit edit
Your implementation of Equals will return true for any non-null instance of your class. Though the syntax makes me think that you aren't, you may be confusing the is C# keyword (which confirms type) with the is keyword in VB.NET (which confirms referential equality). If that is indeed the case, then you can make an explicit reference comparison in C# by using Object.ReferenceEquals(this, obj).
In any case, this is why you are seeing true for Equals, since you're passing in a non-null instance of your class.
Incidentally, your comment about NUnit using Equals is true for the same reason; because operators are not polymorphic, there would be no way for a particular class to define custom equality behavior if the Assert function used ==.
a == b checks if they reference the same object.
a.Equals(b) compares the contents.
This is a link to a Jon Skeet article from 2004 that explains it better.
You pretty much answered your question yourself:
I have also over-ridden Equals() in the subclass implementation, which does a very basic check to see whether or not the passed-in object is null and is of the subclass's type. If both those conditions are met, the objects are deemed to be equal.
The == operator hasn't been overloaded - so it's returning false since a and b are different objects. But a.Equals is calling your override, which is presumably returning true because neither a nor b are null, and they're both of the subclass' type.
So your question was "When can a == b be false and a.Equals(b) true?" Your answer in this case is: when you explicitly code it to be so!
In Java a ==b check if the references of the two objects are equals (rougly, if the two objects are the same object "aliased")
a.equals(b) compare the values represented by the two objects.
They both do the same unless they are specifically overloaded within the object to do something else.
A quote from the Jon Skeet Article mentioned elsewhere.
The Equals method is just a virtual
one defined in System.Object, and
overridden by whichever classes choose
to do so. The == operator is an
operator which can be overloaded by
classes, but which usually has
identity behaviour.
The keyword here is USUALLY. They can be written to do whatever the underlying class wishes and in no way do they have to do the same.
The "==" operate tests absolute equality (unless overloaded); that is, it tests whether two objects are the same object. That's only true if you assigned one to the other, ie.
MyObject a = MyObject.Create();
MyObject b = a;
Just setting all the properties of two objects equal doesn't mean the objects themselves are. Under the hood, what the "==" operator is comparing is the addresses of the objects in memory. A practical effect of this is that if two objects are truly equal, changing a property on one of them will also change it on the other, whereas if they're only similar ("Equals" equal), it won't. This is perfectly consistent once you understand the principle.
I believe that a == b will check if the referenced object is the same.
Usually to see if the value is the same a.Equals(b) is used (this often needs to be overridden in order to work).

Categories

Resources