C#  

The Hidden Costs of Null Checks in Generic C# Code

Introduction

Generic programming in C# gives us the power to write flexible, reusable code. But with that power comes some subtle performance pitfalls, especially when checking if a value is null in a generic method. What looks like a simple null check can turn into a silent performance and memory issue. Let’s see why—and how to do it the right way.

The Naive Approach: value == null

A beginner-friendly check might look like this.

public bool IsNull<T>(T value)
{
    return value == null;
}

At first glance, this works fine, especially for reference types like string, object, or custom classes. However, the problem appears when T is a value type like int, bool, or struct.

The Boxing Problem

In C#, value types normally live on the stack, which is fast and efficient. But when you use == null on a value type in a generic method, the compiler has to box it. That means:

  • The value is wrapped in an object.
  • It’s moved from the stack to the heap.
  • It now behaves like a reference type (temporarily).

This process takes extra time and uses more memory.

Example

IsNull<int>(0);

This call causes boxing. Even though it returns false (since 0 is not null), the system still has to allocate memory on the heap and clean it up later, wasting performance.

The Right Way: EqualityComparer<T>.Default

C# provides a better option.

public bool IsNull<T>(T value)
{
    return EqualityComparer<T>.Default.Equals(value, default(T));
}

Why This Works?

  • EqualityComparer<T>. The default gives you the best way to compare values for type T.
  • It avoids boxing for value types.
  • It correctly checks null for reference types.
  • default(T) returns the default value of type.
    • null for reference types.
    • 0, false, or an empty struct for value types.

Examples

IsNull<string>(null);    // true
IsNull<int>(0);          // true
IsNull<bool>(false);     // true
IsNull<int>(5);          // false

// Modern C# Bonus: is null
// From C# 7.0 onward, we can use:

public bool IsNull<T>(T value) where T : class?
{
    return value is null;
}

This works great for reference types and nullable value types, and avoids boxing. But for non-nullable value types, a null value will always return false.

Combo Option

If you want to cover both null and default values in one method.

public bool IsNull<T>(T value)
{
    return value is null || EqualityComparer<T>.Default.Equals(value, default(T));
}

Difference between value == null VS EqualityComparer<T>.Default.Equals(value, default(T)) VS value is null (C# 7+)
 

Approach Reference Types Value Types (Non-Nullable) Value Types (Nullable) Memory Usage (Value Types)
value == null Fast Boxing occurs Boxing occurs Higher due to boxing
EqualityComparer<T>.Default.Equals(value, default(T)) Good Efficient value comparison Efficient null check Lower, avoids boxing
value is null (C# 7+) Fast Always false Efficient null check Lower, avoids boxing


Conclusion

Null checks in generic C# code are not as simple as they seem. Using value == null can silently hurt performance due to boxing, especially with value types. Instead, use:

  • EqualityComparer<T>.Default.Equals(value, default(T)) for safe and efficient checks across all types.
  • is null for clean null checks with reference and nullable types (C# 7+).

By being aware of these small details, you write faster, cleaner, and more memory-efficient code, especially in performance-critical areas.