C# is not null: Pattern Matching for Null Checks
c# is not null: Learn how the C# is not null pattern provides a concise, type-safe way to check for null values, avoiding operator overload issues.
When you write a null check in C#, the expression x is not null has become a common alternative to x != null. The pattern-based check is more than a stylistic choice: it bypasses overloaded operators and works consistently across reference types and nullable value types. This article explains how c# is not null behaves, where it fits in the language, and how to use it effectively.
The is not null Pattern in C#
The is not null pattern is part of C# 9's extended pattern matching. It checks whether an expression is not null using the constant pattern null. The syntax is straightforward:
if (name is not null) { Console.WriteLine(name.Length); }
This is equivalent to name != null for most practical purposes, but the pattern-based version has a subtle advantage: it does not invoke any user-defined != operator. If the type of name overloads != to perform custom logic, name != null might not behave as a simple reference comparison. The is not null pattern always performs a direct null check, regardless of operator overloading.
Why is not null Differs from != null
The most important difference is operator overloading. Consider a type that overloads == and != to compare by value:
public class Money { public decimal Amount { get; set; } public string Currency { get; set; } public static bool operator ==(Money left, Money right) => left?.Amount == right?.Amount && left?.Currency == right?.Currency; public static bool operator !=(Money left, Money right) => !(left == right); }
If you write money != null, the overloaded operator runs. If money is null, the operator receives null for both sides, and the comparison logic might throw a NullReferenceException if it dereferences the operands. The is not null pattern avoids this entirely because it is a pattern match, not an operator call. It checks the underlying reference directly.
This makes is not null the safer choice when you are working with types that overload comparison operators. It also makes the intent clearer: you are checking for a null reference, not performing a semantic equality comparison.
Using is not null in Property Patterns
The is not null pattern composes with other patterns. A common use is the property pattern, which lets you check nested properties without explicit null checks:
if (customer is { Address: not null }) { // Address is not null here }
This is equivalent to customer?.Address != null, but it is more readable and keeps the null check close to the property access. You can combine multiple property checks:
if (order is { Customer: not null, Total: > 100 }) { // Process large order from a known customer }
The compiler understands the pattern and may even provide flow analysis to avoid redundant checks. This style is particularly useful in switch expressions and if statements where you want to validate several fields at once.
Null Checks in Expressions and Conditions
The is not null pattern works in any expression context, not just if statements. You can use it in ternary expressions, null-coalescing chains, and even in switch expressions:
string label = name is not null ? name.ToUpperInvariant() : "unknown"; string category = value switch { not null when value.Length > 10 => "long", not null => "short", null => "missing" };
In a switch expression, not null is a pattern that matches any non-null value. This is more concise than writing _ when value != null. The pattern also works with nullable value types: int? maybe = 5; if (maybe is not null) correctly checks whether the underlying value exists, without boxing or calling HasValue explicitly.
Performance and Compiler Behavior
From a runtime perspective, is not null is a simple null check. For reference types, the compiler emits a ldnull and ceq or a similar IL sequence, which is the same as a direct comparison. For nullable value types, the pattern translates to a check of the HasValue property, which is also efficient.
The main performance benefit is that you avoid invoking overloaded operators. If a type's != operator performs complex logic, using is not null eliminates that overhead. There is no boxing, no virtual call, and no additional allocation. The pattern is also predictable: it always means "the reference is not null" or "the nullable has a value," regardless of the type's custom equality semantics.
Because the compiler treats is not null as a pattern, it can perform flow analysis. For example, after if (x is not null), the compiler knows x is not null in the true branch, which can help avoid nullability warnings in code that uses nullable reference types.
Compatibility and Language Versions
The is not null pattern requires C# 9 or later. It is available in .NET 5 and newer versions, as well as in .NET Core 3.1 with the latest SDK if you set the language version. If you are working with an older C# compiler, you need to use != null or ReferenceEquals instead.
For nullable value types, the pattern works the same way across versions that support C# 9. The syntax is part of the language, not the runtime, so it works on .NET Framework as long as the compiler supports it. However, the nullable reference type annotations are a separate feature and require .NET Core 3.0 or later for full support.
When migrating existing code, you can replace x != null with x is not null safely for most types. The only exception is if you intentionally rely on an overloaded != operator to perform a custom null check—which is rare and usually a design flaw.
Common Pitfalls and Edge Cases
The is not null pattern is straightforward, but a few edge cases can surprise developers.
For nullable value types, int? x = null; x is not null is false, and x is null is true. This is intuitive. However, when you use the pattern with a nullable value type that has a value, the pattern matches the underlying value, not the nullable wrapper. This means you cannot use is not null to extract the value directly; you still need .Value or the ?? operator.
Another edge case is the not pattern combined with type patterns. For example, x is not string matches any value that is not a string, including null. This is different from x is not null, which only checks for null. Mixing these up can lead to logical errors.
Finally, be careful with the is not null pattern in switch expressions when the input type is a nullable value type. The pattern not null matches the case where HasValue is true, but the switch arm receives the underlying value, not the nullable. This is usually what you want, but it can cause subtle differences if you expect the nullable wrapper.
In practice, c# is not null is a reliable, readable, and safe way to perform null checks. It avoids operator overloads, works consistently across types, and integrates with modern C# pattern matching features. Use it as your default null check in code that targets C# 9 or later.