Back to Blog
C#

C# ReferenceEquals: When to Use It and How It Works

c# referenceequals: Learn how ReferenceEquals compares object identity in C#, its boxing behavior, and when to use it instead of == or Equals.

ReferenceEquals
Diagram showing two object references pointing to the same memory location, illustrating ReferenceEquals in C#.

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

What ReferenceEquals Checks

In C#, object.ReferenceEquals is a static method that determines whether two object references point to the same underlying object instance. It does not call any overridden Equals method and does not consider value equality. The method is defined as:

public static bool ReferenceEquals(object objA, object objB)

Because the parameters are typed as object, any value type passed to this method is boxed before the comparison. This boxing behavior is a common source of confusion and subtle bugs.

ReferenceEquals vs == vs Equals

The == operator and the Equals method are polymorphic. Their behavior depends on the compile-time type of the operands and whether the type overrides them. ReferenceEquals is static and always compares identity.

For reference types, == typically compares references unless the type overloads the operator. For example, string overloads == to compare string content, not references. ReferenceEquals ignores such overloads.

Equals is virtual and can be overridden to provide value equality. ReferenceEquals bypasses all overrides.

Consider this example:

object a = new object(); object b = a; Console.WriteLine(ReferenceEquals(a, b)); // True string s1 = "hello"; string s2 = string.Copy(s1); // creates a new string Console.WriteLine(s1 == s2); // True (value equality) Console.WriteLine(ReferenceEquals(s1, s2)); // False (different instances)

The string.Copy method is obsolete in .NET Core, but it demonstrates the point. In modern .NET, you can use new string(s1.ToCharArray()) or just rely on the fact that two identical string literals may be interned.

Value Types and Boxing

When you pass a value type to ReferenceEquals, the value is boxed into a new object. That means two separate value type variables will never be reference-equal, even if they have identical values. For example:

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

Each boxing operation creates a new object. Even if you box the same variable twice, you get two different references:

int x = 5; object boxed1 = x; object boxed2 = x; Console.WriteLine(ReferenceEquals(boxed1, boxed2)); // False

This is because each boxing operation allocates a new box. There is no caching for general value types. The only exception is the Nullable<T> case where a null value boxes to null, but non-null values still box.

Strings and Interning

The .NET runtime interns string literals. Two string literals with the same content often reference the same interned instance. However, strings created at runtime are not guaranteed to be interned. This makes ReferenceEquals on strings unreliable for equality checks.

string literal1 = "hello"; string literal2 = "hello"; Console.WriteLine(ReferenceEquals(literal1, literal2)); // True (interned) string dynamic1 = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); string dynamic2 = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); Console.WriteLine(ReferenceEquals(dynamic1, dynamic2)); // False (usually)

The behavior for dynamic strings depends on runtime decisions and should never be used for content comparison. Use string.Equals or == for strings.

When to Use ReferenceEquals

ReferenceEquals is useful when you need to check object identity, not value equality. Common scenarios include:

  • Caching or memoization: verifying that a cached object is the same instance.
  • Event handlers: removing a handler by reference to ensure you remove the exact delegate instance.
  • Testing frameworks: asserting that two objects are the same instance.
  • Implementing custom Equals methods: you can start with ReferenceEquals(this, obj) to short-circuit identity checks.

For example, in a custom Equals override:

public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; if (obj is null) return false; if (GetType() != obj.GetType()) return false; // Compare fields... }

This pattern avoids unnecessary field comparisons when the same instance is passed.

Performance and Runtime Cost

ReferenceEquals is a simple pointer comparison under the hood. It does not invoke virtual methods or require type-specific logic. Its cost is minimal, comparable to a direct reference comparison. However, passing value types to it incurs boxing, which allocates memory and copies the value. That overhead can be significant in hot paths.

If you need to compare value types by reference, you should not use ReferenceEquals at all. Instead, consider using EqualityComparer<T>.Default or constrain the type to a reference type.

Common Mistakes and Edge Cases

A frequent mistake is assuming ReferenceEquals works like == for value types. It does not. Another is using it to compare strings for equality. Always use string.Equals for content.

Another edge case: ReferenceEquals returns true when both arguments are null. That is consistent because null is a single reference.

Console.WriteLine(ReferenceEquals(null, null)); // True

Also, note that ReferenceEquals is not virtual, so it cannot be overridden. It is a static method on object, so you cannot change its behavior.

In generic code, ReferenceEquals may not behave as expected if T is a value type. For example:

public bool IsSame<T>(T a, T b) where T : class { return ReferenceEquals(a, b); }

If you remove the class constraint, the method will box value types and always return false for non-null values. That is rarely what you want.

When ReferenceEquals Is Not Enough

For types that override Equals but also need identity semantics, ReferenceEquals is the only way to bypass the override. However, be careful when using it in a context where the object may be a transparent proxy or a remoting object, because identity semantics may differ across boundaries. In modern .NET, this is less of a concern, but it's worth remembering.

In summary, ReferenceEquals is a precise tool for identity checks. Use it when you need to know if two references point to the same object, and avoid it for value equality or string content comparisons.

c# referenceequals: Practical Usage and Code Examples | RYUSLOG DEV