Back to Blog
C#

C# Object Equals vs ReferenceEquals: Key Differences

c# object equals vs referenceequals: Understand the difference between Object.Equals and ReferenceEquals in C# and when to override Equals for value semantics.

C#Object.EqualsReferenceEqualsvalue equalityreference equality
Comparison of Object.Equals and ReferenceEquals in C# showing two objects with same value but different references.

c# object equals vs referenceequals requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When comparing objects in C#, Object.Equals and ReferenceEquals answer different questions. ReferenceEquals checks whether two references point to the same instance. Object.Equals is a virtual method that can be overridden to define value equality. The distinction matters when you work with custom types, collections, or equality-based operations like HashSet<T> or Dictionary<TKey, TValue>.

What Object.Equals Does by Default

For reference types, the default implementation of Object.Equals behaves exactly like ReferenceEquals. It returns true only when both references point to the same object. This is reference equality. For value types, the default implementation uses reflection to compare all fields, which is slower and often overridden by the type itself.

Consider this example:

public class Person { public string Name { get; set; } } var p1 = new Person { Name = "Alice" }; var p2 = new Person { Name = "Alice" }; Console.WriteLine(p1.Equals(p2)); // False Console.WriteLine(ReferenceEquals(p1, p2)); // False

Both return false because p1 and p2 are distinct instances. The default Equals for a reference type does not compare field values.

What ReferenceEquals Does

ReferenceEquals is a static method that checks reference identity. It does not call any virtual method, so it always compares the object references themselves. It returns true only if both arguments point to the exact same object in memory. For value types, ReferenceEquals always returns false because value types are boxed when passed as object, creating separate boxed instances.

int x = 42; int y = 42; Console.WriteLine(ReferenceEquals(x, y)); // False

Even though x and y have the same value, they are boxed into separate objects, so reference equality fails.

When Overriding Equals Changes Behavior

When you override Equals in a class, you define what it means for two objects to be equal. This is typically used to implement value semantics, where two separate instances with the same data are considered equal. The override must be consistent with GetHashCode to work correctly in hash-based collections.

public class Person { public string Name { get; set; } public override bool Equals(object obj) { if (obj is Person other) { return Name == other.Name; } return false; } public override int GetHashCode() { return Name?.GetHashCode() ?? 0; } }

Now p1.Equals(p2) returns true when both have the same Name. However, ReferenceEquals(p1, p2) still returns false because the references are different. This distinction is critical: overriding Equals does not change ReferenceEquals.

Value Equality vs Reference Equality in Practice

The choice between value equality and reference equality depends on the domain model. Entities with a stable identity, such as a database record, typically use reference equality. Value objects, such as a Money or Coordinate type, should implement value equality.

For example, a Coordinate class that overrides Equals to compare X and Y values allows two separately created coordinates with the same values to be treated as equal. This is useful in geometric calculations or when using coordinates as dictionary keys.

public class Coordinate { public int X { get; } public int Y { get; } public Coordinate(int x, int y) { X = x; Y = y; } public override bool Equals(object obj) => obj is Coordinate other && X == other.X && Y == other.Y; public override int GetHashCode() => HashCode.Combine(X, Y); }

With this implementation, new Coordinate(1, 2).Equals(new Coordinate(1, 2)) returns true, while ReferenceEquals returns false. This allows the type to be used in a HashSet<Coordinate> without duplicates based on value.

Common Pitfalls with Equals and ReferenceEquals

One common mistake is assuming that == behaves like Equals. For reference types, == uses reference equality unless overloaded. For value types, == is not available unless the type overloads it. Another pitfall is overriding Equals without overriding GetHashCode, which breaks dictionary lookups.

Another subtle issue arises when using ReferenceEquals with value types. Because value types are boxed, ReferenceEquals will almost always return false even for the same variable:

int a = 10; object boxedA = a; object boxedB = a; Console.WriteLine(ReferenceEquals(boxedA, boxedB)); // False

Each boxing operation creates a new object. This is why ReferenceEquals is rarely useful for value types.

Performance and Allocation Considerations

ReferenceEquals is a direct pointer comparison, so it is extremely fast and does not involve virtual dispatch. Object.Equals may involve a virtual call, and if overridden, it could perform field comparisons, which is more expensive. For value types, the default Equals uses reflection, which is slow; most built-in value types override it with optimized implementations.

When you override Equals for a custom type, you control the cost. Comparing a few fields is usually cheap, but if the type contains large collections, the comparison can be expensive. Consider whether reference equality is sufficient for your use case before implementing value equality, because the latter adds complexity and maintenance overhead.

In high-performance scenarios, such as tight loops that compare many objects, ReferenceEquals can be a micro-optimization when you only need to check identity. However, if you need value semantics, you cannot avoid the cost of field comparison.

Choosing the Right Comparison for Your Type

The decision between Object.Equals and ReferenceEquals is not about choosing one over the other globally; it is about understanding what each method does and using them appropriately. ReferenceEquals is a tool for identity checks, while Object.Equals is the mechanism for equality semantics that can be customized.

For most application code, you will rely on Object.Equals and its overrides to support value equality in collections and LINQ operations. Use ReferenceEquals when you specifically need to know whether two variables point to the same instance, such as when implementing a cache or tracking object lifetime.

If you are designing a type that should have value semantics, override Equals and GetHashCode together. If the type is meant to be an entity with identity, leave the default reference behavior and do not override Equals. This keeps the code predictable and avoids subtle bugs where two separate instances are unexpectedly considered equal.

c# object equals vs referenceequals: Practical Usage and Cod | RYUSLOG DEV