Overriding Equals() but not checking all fields - what will happen? - c#

If I override Equals and GetHashCode, how do I decide which fields to compare? And what will happen if I have two objects with two fields each, but Equals only checks one field?
In other words, let's say I have this class:
class EqualsTestClass
{
public string MyDescription { get; set; }
public int MyId { get; set; }
public override bool Equals(object obj)
{
EqualsTestClass eq = obj as EqualsTestClass;
if(eq == null) {
return false;
} else {
return MyId.Equals(eq.MyId);
}
}
public override int GetHashCode()
{
int hashcode = 23;
return (hashcode * 17) + MyId.GetHashCode();
}
}
I consider two objects Equal if they have the same MyId. So if the Id is equal but the description is different, they are still considered equal.
I just wonder what the pitfalls of this approach are? Of course, a construct like this will behave as expected:
List<EqualsTestClass> test = new List<EqualsTestClass>();
EqualsTestClass eq1 = new EqualsTestClass();
eq1.MyId = 1;
eq1.MyDescription = "Des1";
EqualsTestClass eq2 = new EqualsTestClass();
eq2.MyId = 1;
eq2.MyDescription = "Des2";
test.Add(eq1);
if (!test.Contains(eq2))
{
// Will not be executed, as test.Contains is true
test.Add(eq2);
}
As eq2 is value-equal to eq1, it will not be added. But that is code that I control, but I wonder if there is code in the framework that could cause unexpected problems?
So, should I always add all public Fields in my Equals() Comparison, or what are the guidelines to avoid a nasty surprise because of some bad Framework-Mojo that was completely unexpected?

The reason for overriding Equals() is that you define, what it means for two instances to be equal. In some cases that means that all fields must be equal, but it doesn't have to. You decide.
For more information see the documentation and this post.

I don't think you need to worry about the Framework in this instance.
If you as the Class Designer consider two instances of that class to be equal if they share the same MyId, then, you only need to test MyId in your overriden Equals() and GetHashCode() methods.

You only need to check for the fields that are required to match, if all that needs to match is the ID then go with that.

A question: If I override Equals and GetHashCode, how do i decide which fields I compare?
It depends on what you are trying to accomplish. If you are trying to see if the objects are exactly the same you should compare all of them. If you have some 'key' and you only want to know if they are the same 'object', even if other data is different then just check the 'key' values.
And what will happen if I have two objects with two fields each, but Equals only checks one field?
Then you will have an equality method that just checks to see if the 'key' is the same, and could potentially have multiple 'equal' objects that have internal variances.

Others have said this is perfectly valid and expected, and it's exactly how Equals is supposed to operate. So there's no problem with it as a class.
I'd be very slightly wary of this as an API. Forgive me if it isn't what was intended: in that case this is just a note of caution to others.
The potential problem is that users of the API will naturally expect equal objects to "be the same". This isn't part of the contract of equality, but it is part of the common-sense meaning of the word. The class looks a bit like a binary tuple, but isn't one, so that should be for sensible reasons.
An example of such a sensible reason is that one field is a "visible implementation detail", like the max load factor on a hashtable-based container. An example of a risky (although tempting) reason is "because I added the description in afterwards and didn't want to change the Equals method in case it broke something".
So it's completely valid to do something a bit counter-intuitive, especially if you clearly document that the behaviour might be surprising. Such Equals methods have to be supported, because banning them would be crazy in cases where a field is obviously irrelevant. But it should be clear why it makes sense to create two ID-description pairs with the same ID and different descriptions, but it doesn't make sense to add them both to a container (like HashSet) which uses Equals/HashCode to prevent duplicate entries.

Your example is checking to see if a list(of EqualsTestClass) contains an object of the same type with the same property values. Another way to accomplish this task without overriding equals (and the traditional understanding of equals) is to use a custom comparer. It would look something like this (in VB):
Public Class EqualsTestComparer
Implements IEqualityComparer(Of EqualsTestClass)
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals
If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then
Return True
Else
Return False
End If
End Function
Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode
Return obj.ToString.ToLower.GetHashCode
End Function
End Class
Then in your routine you simply use the custom comparer:
If Not test.Contains(eq2, New EqualsTestComparer) Then
//Do Stuff
End if

Related

How to implement GetHashCode for this situation?

I'm trying to implement an IEqualityComparer<string> which basically compares two strings in a way that,(let's assume we have two strings x and y) if x starts with y or y starts with x they should be treated as equal.
public bool Equals(string x, string y)
{
return x.StartsWith(y) || y.StartsWith(x);
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
Ofcourse implementing the Equals method is pretty easy.But the GetHashCode is not, I couldn't think any way to implement it correctly.I have written a test program like this:
string[] values = {"hell", "hello", "foo", "fooooo"};
var result = values.Distinct(new StringComparer());
foreach(var x in result)
Console.WriteLine(x);
And I get the wrong result because of GetHashCode:
hell
hello
foo
fooooo
Obviously I can force calling Equals method by returning same value from the GetHashCode for all values but I wanna know if there is another way to implement it because the performance is critical. Is there a way to implement GetHashCode method correctly for my situation ?
Note: I know it is vague but I couldn't find a better title, if you have a better idea you are free to edit.
Edit: I'm going to use this logic with web urls. In my situation first 20 characters are equal. For example:
http://www.foo.com/bar?id=3
http://www.foo.com/bar?id=3&fooId=23
The issue is in your definition of equality: Equality must be transitive. But it is not in your case. Take the following three values:
* f
* freeze
* foo
Then f == freeze, and foo == f, but freeze != foo.
See also MSDN on Implementing the Equals Method, which says:
(x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.
A proper definition of equality produces distinct sets of values that are considered equal. If you had those, you could define a "canonical" representation for each set and calculate the hash of the canonical value so each set would have its hash code. But this only works with an operation that is transitive (as well as commutative and reflexive, these two properties are covered by your definition).
Since your definition of equality is not transitive you cannot define such sets so you can't find a proper hash code either.
But it raises other questions, too. Taking your example:
string[] values = { "hell", "hello", "foo", "fooooo" };
var result = values.Distinct(new StringComparer());
Which values do you expect to go into your result? Do you always want the shortest version? This will not be guaranteed by your code, the result will depend on the internal implementation of Distinct.
Implementing an EqualityComparer might possibly be a sub-optimal approach to your actual issue. What are you trying to achieve?
As strings are equal to each other depending on what string you compare them with, any string can be equal to another. Thus there is only one way to implement the GetHashCode method; return the same value for all strings:
public int GetHashCode(string obj) {
return 0;
}
This will naturally give a horrible distribution. A dictionary will have a O(n) lookup time instead of O(1), but it works, and it's the only way to make it work for such an equality comparison.

Why are you expected to override GetHashCode and Equals when overloading the equality operator?

Failing to override GetHashCode and Equals when overloading the equality operator causes the compiler to produce warnings. Why would it be a good idea to change the implementation of either? After reading Eric Lippert's blog post on GetHashCode it's seems like there probably aren't many useful alternatives to GetHashCode's base implementation, why does the compiler I encourage you to change it?
Let's suppose you are implementing a class.
If you are overloading == then you are producing a type that has value equality as opposed to reference equality.
Given that, now the question is "how desirable is it to have a class that implements reference equality in .Equals() and value equality in ==?" and the answer is "not very desirable". That seems like a potential source of confusion. (And in fact, the company that I now work for, Coverity, produces a defect discovery tool that checks to see if you are confusing value equality with reference equality for precisely this reason. Coincidentally I was just reading the spec for it when I saw your question!)
Moreover, if you are going to have a class that implements both value and reference equality, the usual way to do it is to override Equals and leave == alone, not the other way around.
Therefore, given that you have overloaded ==, it is strongly suggested that you also override Equals.
If you are overriding Equals to produce value equality then you are required to override GetHashCode to match, as you know if you've read my article that you linked to.
If you don't override Equals() when you override == you will have some amazingly bad code.
How would you feel about this happening?
if (x == y)
{
if (!x.Equals(y))
throw new InvalidOperationException("Wut?");
}
Here's an example. Given this class:
class Test
{
public int Value;
public string Name;
public static bool operator==(Test lhs, Test rhs)
{
if (ReferenceEquals(lhs, rhs))
return true;
if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
return false;
return lhs.Value == rhs.Value;
}
public static bool operator!=(Test lhs, Test rhs)
{
return !(lhs == rhs);
}
}
This code will behave oddly:
Test test1 = new Test { Value = 1, Name = "1" };
Test test2 = new Test { Value = 1, Name = "2" };
if (test1 == test2)
Console.WriteLine("test1 == test2"); // This gets printed.
else
Console.WriteLine("test1 != test2");
if (test1.Equals(test2))
Console.WriteLine("test1.Equals(test2)");
else
Console.WriteLine("NOT test1.Equals(test2)"); // This gets printed!
You do NOT want this!
My guess is that the compiler takes its clues from your actions, and decides that since you find it important to provide an alternative implementation of the equality operator, then you probably want the object equality to remain consistent with your new implementation of ==. After all, you do not want the two equality comparisons to mean drastically different things, otherwise your program would be hard to understand even on a very basic level. Therefore, the compiler thinks that you should redefine Equals as well.
Once you provide an alternative implementation Equals, however, you need to modify GetHashCode to stay consistent with the equality implementation. Hence the compiler warns you that your implementation might be incomplete, and suggests overriding both Equals and GetHashCode.
If you don't overload the Equals method too, then using it might give different results from the ones you'd get with the operator. Like, if you overload = for integers...
int i = 1;
(1 == 1) == (i.Equals(1))
Could evaluate to false.
For the same reason, you should reimplement the GetHashCode method so you don't mess up with hashtables and such other structures that rely on hash comparisons.
Notice I'm saying "might" and "could", not "will". The warnings are there just as a reminder that unexpected things might happen if you don't follow its suggestions. Otherwise you'd get errors instead of warnings.
The documentation is pretty clear about this:
The GetHashCode method can be overridden by a derived type. Value
types must override this method to provide a hash function that is
appropriate for that type and to provide a useful distribution in a
hash table. For uniqueness, the hash code must be based on the value
of an instance field or property instead of a static field or
property.
Objects used as a key in a Hashtable object must also override the
GetHashCode method because those objects must generate their own hash
code. If an object used as a key does not provide a useful
implementation of GetHashCode, you can specify a hash code provider
when the Hashtable object is constructed. Prior to the .NET Framework
version 2.0, the hash code provider was based on the
System.Collections.IHashCodeProvider interface. Starting with version
2.0, the hash code provider is based on the System.Collections.IEqualityComparer interface.

Why does the Equals implementation for anonymous types compare fields?

I'm just wondering why designers of the language decided to implement Equals on anonymous types similarly to Equals on value types. Isn't it misleading?
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static void ProofThatAnonymousTypesEqualsComparesBackingFields()
{
var personOne = new { Name = "Paweł", Age = 18 };
var personTwo = new { Name = "Paweł", Age = 18 };
Console.WriteLine(personOne == personTwo); // false
Console.WriteLine(personOne.Equals(personTwo)); // true
Console.WriteLine(Object.ReferenceEquals(personOne, personTwo)); // false
var personaOne = new Person { Name = "Paweł", Age = 11 };
var personaTwo = new Person { Name = "Paweł", Age = 11 };
Console.WriteLine(personaOne == personaTwo); // false
Console.WriteLine(personaOne.Equals(personaTwo)); // false
Console.WriteLine(Object.ReferenceEquals(personaOne, personaTwo)); // false
}
At first glance, all printed boolean values should be false. But lines with Equals calls return different values when Person type is used, and anonymous type is used.
Anonymous type instances are immutable data values without behavior or identity. It doesn't make much sense to reference-compare them. In that context I think it is entirely reasonable to generate structural equality comparisons for them.
If you want to switch the comparison behavior to something custom (reference comparison or case-insensitivity) you can use Resharper to convert the anonymous type to a named class. Resharper can also generate equality members.
There is also a very practical reason to do this: Anonymous types are convenient to use as hash keys in LINQ joins and groupings. For that reason they require semantically correct Equals and GetHashCode implementations.
For the why part you should ask the language designers...
But I found this in Eric Lippert’s article about Anonymous Types Unify Within An Assembly, Part Two
An anonymous type gives you a convenient place to store a small
immutable set of name/value pairs, but it gives you more than that. It
also gives you an implementation of Equals, GetHashCode and, most
germane to this discussion, ToString. (*)
Where the why part comes in the note:
(*) We give you Equals and GetHashCode so that you can use instances
of anonymous types in LINQ queries as keys upon which to perform
joins. LINQ to Objects implements joins using a hash table for
performance reasons, and therefore we need correct implementations of
Equals and GetHashCode.
The official answer from the C# Language Specification (obtainable here):
The Equals and GetHashcode methods on anonymous types override the methods inherited from object, and are defined in terms of the Equals and GetHashcode of the properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal.
(My emphasis)
The other answers explain why this is done.
It's worth noting that in VB.Net the implementation is different:
An instance of an anonymous types that has no key properties is equal only to itself.
The key properties must be indicated explicitly when creating an anonymous type object. The default is: no key, which can be very confusing for C# users!
These objects aren't equal in VB, but would be in C#-equivalent code:
Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}
These objects evaluate to "equal":
Dim prod3 = New With {Key .Name = "paperclips", .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", .Price = 2.00}
Because it gives us something that's useful. Consider the following:
var countSameName = from p in PersonInfoStore
group p.Id by new {p.FirstName, p.SecondName} into grp
select new{grp.Key.FirstName, grp.Key.SecondName, grp.Count()};
The works because the implementation of Equals() and GetHashCode() for anonymous types works on the basis of field-by-field equality.
This means the above will be closer to the same query when run against at PersonInfoStore that isn't linq-to-objects. (Still not the same, it'll match what an XML source will do, but not what most databases' collations would result in).
It means we don't have to define an IEqualityComparer for every call to GroupBy which would make group by really hard with anonymous objects - it's possible but not easy to define an IEqualityComparer for anonymous objects - and far from the most natural meaning.
Above all, it doesn't cause problems with most cases.
The third point is worth examining.
When we define a value type, we naturally want a value-based concept of equality. While we may have a different idea of that value-based equality than the default, such as matching a given field case-insensitively, the default is naturally sensible (if poor in performance and buggy in one case*). (Also, reference equality is meaningless in this case).
When we define a reference type, we may or may not want a value-based concept of equality. The default gives us reference equality, but we can easily change that. If we do change it, we can change it for just Equals and GetHashCode or for them and also ==.
When we define an anonymous type, oh wait, we didn't define it, that's what anonymous means! Most of the scenarios in which we care about reference equality aren't there any more. If we're going to be holding an object around for long enough to later wonder if it's the same as another one, we're probably not dealing with an anonymous object. The cases where we care about value-based equality come up a lot. Very often with Linq (GroupBy as we saw above, but also Distinct, Union, GroupJoin, Intersect, SequenceEqual, ToDictionary and ToLookup) and often with other uses (it's not like we weren't doing the things Linq does for us with enumerables in 2.0 and to some extent before then, anyone coding in 2.0 would have written half the methods in Enumerable themselves).
In all, we gain a lot from the way equality works with anonymous classes.
In the off-chance that someone really wants reference equality, == using reference equality means they still have that, so we don't lose anything. It's the way to go.
*The default implementation of Equals() and GetHashCode() has an optimisation that let's it use a binary match in cases where it's safe to do so. Unfortunately there's a bug that makes it sometimes mis-identify some cases as safe for this faster approach when they aren't (or at least it used to, maybe it was fixed). A common case is if you have a decimal field, in a struct, then it'll consider some instances with equivalent fields as unequal.

When do we do GetHashCode() for a Dictionary?

I have used Dictionary(TKey, TValue) for many purposes. But I haven't encountered any scenario to implement GetHashCode() which I believe is because my keys were of primary types like int and string.
I am curious to know the scenarios (real world examples) when one should use a custom object for key and thus implement methods GetHashCode() Equals() etc.
And, does using a custom object for key necessitate implementing these functions?
You should override Equals and GetHashCode whenever the default Object.Equals (tests for reference equality) will not suffice. This happens, for example, when the type of your key is a custom type and you want two keys to be considered equal even in cases when they are not the same instance of the custom type.
For example, if your key is as simple as
class Point {
public int X { get; set; }
public int Y { get; set; }
}
and you want two Points two be considered equal if their Xs are equal and their Ys are equal then you will need to override Equals and GetHashCode.
Just to make it clear: There is one important thing about Dictionary<TKey, TValue> and GetHashCode(): Dictionary uses GetHashCode to determine if two keys are equal i.e. if <TKey> is of custom type you should care about implementing GetHashCode() carefully. As Andrew Hare pointed out this is easy, if you have a simple type that identifies your custom object unambiguously. In case you have a combined identifier, it gets a little more complicated.
As example consider a complex number as TKey. A complex number is determined by its real and its imaginary part. Both are of simple type e.g. double. But how would you identify if two complex numbers are equal? You implement GetHashCode() for your custom complex type and combine both identifying parts.
You find further reading on the latter here.
UPDATE
Based on Ergwun's comment I checked the behavior of Dictionary<TKey, TValue>.Add with special respect to TKey's implementation of Equals(object) and GetHashCode(). I
must confess that I was rather surprised by the results.
Given two objects k1 and k2 of type TKey, two arbitrary objects v1 and v2 of type TValue, and an empty dictionary d of type Dictionary<TKey, TValue>, this is what happens when adding v1 with key k1 to d first and v2 with key k2 second (depending on the implementation of TKey.Equals(object) and TKey.GetHashCode()):
k1.Equals(k2) k1.GetHashCode() == k2.GetHashCode() d.Add(k2, v2)
false false ok
false true ok
true false ok
true true System.ArgumentException
Conclusion: I was wrong as I originally thought the second case (where Equals returns false but both key objects have same hash code) would raise an ArgumentException. But as the third case shows dictionary in some way does use GetHashCode(). Anyway it seems to be good advice that two objects that are the same type and are equal must return the same hash code to ensure that instances Dictionary<TKey, TValue> work correctly.
You have two questions here.
When do you need to implement
GetHashCode()
Would you ever use an object for a dictionary key.
Lets start with 1. If you are writing a class that might possibly be used by someone else, you will want to define GetHashCode() and Equals(), when reference Equals() is not enough. If you're not planning on using it in a dictionary, and it's for your own usage, then I see no reason to skip GetHashCode() etc.
For 2), you should use an object anytime you have a need to have a constant time lookup from an object to some other type. Since GetHashCode() returns a numeric value, and collections store references, there is no penalty for using an Object over an Int or a string (remember a string is an object).
One example is when you need to create a composite key (that is a key comprised of more that one piece of data). That composite key would be a custom type that would need to override those methods.
For example, let's say that you had an in-memory cache of address records and you wanted to check to see if an address was in cache to save an expensive trip to the database to retrieve it. Let's also say that addresses are unique in terms of their street 1 and zip code fields. You would implement your cache with something like this:
class AddressCacheKey
{
public String StreetOne { get; set; }
public String ZipCode { get; set; }
// overrides for Equals and GetHashCode
}
and
static Dictionary<AddressCacheKey,Address> cache;
Since your AddressCacheKey type overrides the Equals and GetHashCode methods they would be a good candidate for a key in the dictionary and you would be able to determine whether or not you needed to take a trip to the database to retrieve a record based on more than one piece of data.

== or .Equals()

Why use one over the other?
== is the identity test. It will return true if the two objects being tested are in fact the same object. Equals() performs an equality test, and will return true if the two objects consider themselves equal.
Identity testing is faster, so you can use it when there's no need for more expensive equality tests. For example, comparing against null or the empty string.
It's possible to overload either of these to provide different behavior -- like identity testing for Equals() --, but for the sake of anybody reading your code, please don't.
Pointed out below: some types like String or DateTime provide overloads for the == operator that give it equality semantics. So the exact behavior will depend on the types of the objects you are comparing.
See also:
http://blogs.msdn.com/csharpfaq/archive/2004/03/29/102224.aspx
#John Millikin:
Pointed out below: some value types like DateTime provide overloads for the == operator >that give it equality semantics. So the exact behavior will depend on the types of the >objects you are comparing.
To elaborate:
DateTime is implemented as a struct. All structs are children of System.ValueType.
Since System.ValueType's children live on the stack, there is no reference pointer to the heap, and thus no way to do a reference check, you must compare objects by value only.
System.ValueType overrides .Equals() and == to use a reflection based equality check, it uses reflection to compare each fields value.
Because reflection is somewhat slow, if you implement your own struct, it is important to override .Equals() and add your own value checking code, as this will be much faster. Don't just call base.Equals();
Everyone else pretty much has you covered, but I have one more word of advice. Every now and again, you will get someone who swears on his life (and those of his loved ones) that .Equals is more efficient/better/best-practice or some other dogmatic line. I can't speak to efficiency (well, OK, in certain circumstances I can), but I can speak to a big issue which will crop up: .Equals requires an object to exist. (Sounds stupid, but it throws people off.)
You can't do the following:
StringBuilder sb = null;
if (sb.Equals(null))
{
// whatever
}
It seems obvious to me, and perhaps most people, that you will get a NullReferenceException. However, proponents of .Equals forget about that little factoid. Some are even "thrown" off (sorry, couldn't resist) when they see the NullRefs start to pop up.
(And years before the DailyWTF posting, I did actually work with someone who mandated that all equality checks be .Equals instead of ==. Even proving his inaccuracy didn't help. We just made damn sure to break all his other rules so that no reference returned from a method nor property was ever null, and it worked out in the end.)
== is generally the "identity" equals meaning "object a is in fact the exact same object in memory as object b".
equals() means that the objects logically equal (say, from a business point of view). So if you are comparing instances of a user-defined class, you would generally need to use and define equals() if you want things like a Hashtable to work properly.
If you had the proverbial Person class with properties "Name" and "Address" and you wanted to use this Person as a key into a Hashtable containing more information about them, you would need to implement equals() (and hash) so that you could create an instance of a Person and use it as a key into the Hashtable to get the information.
Using == alone, your new instance would not be the same.
According to MSDN:
In C#, there are two different kinds of equality: reference equality (also known as identity) and value equality. Value equality is the generally understood meaning of equality: it means that two objects contain the same values. For example, two integers with the value of 2 have value equality. Reference equality means that there are not two objects to compare. Instead, there are two object references and both of them refer to the same object.
...
By default, the operator == tests for reference equality by determining whether two references indicate the same object.
Both Equals and == can be overloaded, so the exact results of calling one or the other will vary. Note that == is determined at compile time, so while the actual implementation could change, which == is used is fixed at compile time, unlike Equals which could use a different implementation based on the run time type of the left side.
For instance string performs an equality test for ==.
Also note that the semantics of both can be complex.
Best practice is to implement equality like this example. Note that you can simplify or exclude all of this depending on how you plan on using you class, and that structs get most of this already.
class ClassName
{
public bool Equals(ClassName other)
{
if (other == null)
{
return false;
}
else
{
//Do your equality test here.
}
}
public override bool Equals(object obj)
{
ClassName other = obj as null; //Null and non-ClassName objects will both become null
if (obj == null)
{
return false;
}
else
{
return Equals(other);
}
}
public bool operator ==(ClassName left, ClassName right)
{
if (left == null)
{
return right == null;
}
else
{
return left.Equals(right);
}
}
public bool operator !=(ClassName left, ClassName right)
{
if (left == null)
{
return right != null;
}
else
{
return !left.Equals(right);
}
}
public override int GetHashCode()
{
//Return something useful here, typically all members shifted or XORed together works
}
}
Another thing to take into consideration: the == operator may not be callable or may have different meaning if you access the object from another language. Usually, it's better to have an alternative that can be called by name.
The example is because the class DateTime implements the IEquatable interface, which implements a "type-specific method for determining equality of instances." according to MSDN.
use equals if you want to express the contents of the objects compared should be equal. use == for primitive values or if you want to check that the objects being compared is one and the same object. For objects == checks whether the address pointer of the objects is the same.
I have seen Object.ReferenceEquals() used in cases where one wants to know if two references refer to the same object
In most cases, they are the same, so you should use == for clarity. According to the Microsoft Framework Design Guidelines:
"DO ensure that Object.Equals and the equality operators have exactly the same semantics and similar performance characteristics."
https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/equality-operators
But sometimes, someone will override Object.Equals without providing equality operators. In that case, you should use Equals to test for value equality, and Object.ReferenceEquals to test for reference equality.
If you do disassemble (by dotPeek for example) of Object, so
public virtual bool Equals(Object obj)
described as:
// Returns a boolean indicating if the passed in object obj is
// Equal to this. Equality is defined as object equality for reference
// types and bitwise equality for value types using a loader trick to
// replace Equals with EqualsValue for value types).
//
So, is depend on type.
For example:
Object o1 = "vvv";
Object o2 = "vvv";
bool b = o1.Equals(o2);
o1 = 555;
o2 = 555;
b = o1.Equals(o2);
o1 = new List<int> { 1, 2, 3 };
o2 = new List<int> { 1, 2, 3 };
b = o1.Equals(o2);
First time b is true (equal performed on value types), second time b is true (equal performed on value types), third time b is false (equal performed on reference types).

Categories

Resources