BRO1148: Check a nullable value type for null instead of calling HasValue¶
x.HasValue becomes x is not null and !x.HasValue becomes x is null, or x != null and x == null, whichever
form BRO1133 asks for. Not a StyleCop rule: from Meziantou's
MA0171. Off by default, also in
the preset.
Example¶
root = true
[*.cs]
dotnet_diagnostic.BRO1148.severity = warning
Before¶
using System;
public class Booking
{
public DateTime? CheckedIn { get; set; }
public DateTime? CheckedOut { get; set; }
public bool IsActive => CheckedIn.HasValue && !CheckedOut.HasValue;
}
After¶
using System;
public class Booking
{
public DateTime? CheckedIn { get; set; }
public DateTime? CheckedOut { get; set; }
public bool IsActive => CheckedIn is not null && CheckedOut is null;
}
Why¶
HasValue and a null check mean the same on a nullable value type (the compiler emits the same HasValue call for
both), so a codebase that writes null checks one way can write them that way for int? too, and a reader doesn't have
to know whether x is a reference or a nullable value. It's a matter of taste, though: many teams prefer HasValue,
which says "nullable value type" at a glance. That's why the rule is off by default.
Configuration¶
The form comes from BRO1133's option, so the two rules never disagree:
[*.cs]
# pattern_matching (default): x is not null / x is null; equality_operator: x != null / x == null
stylebro_null_check_style = pattern_matching
How the fix works¶
It replaces .HasValue with is not null (and removes the ! before a negated one), leaving the expression in front
as it is. The result is already BRO1133's preferred form, so dotnet format needs one run with both rules on.
Not reported¶
- Places where the null check would bind differently: as an operand of an operator that binds as tightly as
==or tighter (b == x.HasValue,!!x.HasValue), or as the receiver of a member (x.HasValue.ToString()). The same places BRO1133 leaves alone. While BRO1405 is on, parentheses aroundx.HasValuedon't count (it removes them). - Patterns where they don't compile:
is not nullbefore C# 9,is nullbefore C# 7, and patterns in expression trees (Expression<Func<int?, bool>> f = n => n.HasValue;;n != nullis fine there). != nullthat would call a user-defined operator: onGuid?,DateTime?orDateTimeOffset?,x != nullnames the underlying type's operator, lifted (harmless at run time, but a banned-API list may forbid it; BRO1133 skips the same).- Other
HasValueproperties (onlySystem.Nullable<T>.HasValue),nameof(x.HasValue), and comments, line breaks or spaces around the.or after the!.
Compared with StyleCop¶
StyleCop has no such rule (its issue #1455 asked for
HasValue -> != null; never implemented). stylebro-migrate leaves it off.