I always used Nullable<>.HasValue
because I liked the semantics. However, recently I was working on someone else's existing codebase where they used Nullable<> != null
exclusively instead.
Is there a reason to use one over the other, or is it purely preference?
int? a; if (a.HasValue) // ...
vs.
int? b; if (b != null) // ...
HasValue
since I think words tend to be more readable than symbols. It's all up to you though, and what fits with your existing style.
.HasValue
makes more sense as it denotes the type is of type T?
rather than a type that can be nullable such as strings.
The compiler replaces null
comparisons with a call to HasValue
, so there is no real difference. Just do whichever is more readable/makes more sense to you and your colleagues.
I prefer (a != null)
so that the syntax matches reference types.
Nullable<>
is not a reference type.
.HasValue
becomes incorrect syntax as soon as it's no longer explicitly Nullable
, which may not be a common case, but if you ever have written a struct for Tuple's sake, and then turned it into a class, you've been in the area that this applies, and with NullableRefs coming up, this will become much more likely to occur.
I did some research on this by using different methods to assign values to a nullable int. Here is what happened when I did various things. Should clarify what's going on. Keep in mind: Nullable<something>
or the shorthand something?
is a struct for which the compiler seems to be doing a lot of work to let us use with null as if it were a class.
As you'll see below, SomeNullable == null
and SomeNullable.HasValue
will always return an expected true or false. Although not demonstrated below, SomeNullable == 3
is valid too (assuming SomeNullable is an int?
).
While SomeNullable.Value
gets us a runtime error if we assigned null
to SomeNullable
. This is in fact the only case where nullables could cause us a problem, thanks to a combination of overloaded operators, overloaded object.Equals(obj)
method, and compiler optimization and monkey business.
Here is a description of some code I ran, and what output it produced in labels:
int? val = null;
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
Ok, lets try the next initialization method:
int? val = new int?();
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
All the same as before. Keep in mind that initializing with int? val = new int?(null);
, with null passed to the constructor, would have produced a COMPILE time error, since the nullable object's VALUE is NOT nullable. It is only the wrapper object itself that can equal null.
Likewise, we would get a compile time error from:
int? val = new int?();
val.Value = null;
not to mention that val.Value
is a read-only property anyway, meaning we can't even use something like:
val.Value = 3;
but again, polymorphous overloaded implicit conversion operators let us do:
val = 3;
No need to worry about polysomthing whatchamacallits though, so long as it works right? :)
Nullable<X>
in VisualStudio 2013 and F12 it, you will see that it only overloads conversion to and from X
, and the Equals(object other)
method. However, I think the == operator uses that method by default, so the effect is the same. I've actually been meaning to update this answer on that fact for a while now, but I'm lazy and/or busy. This comment will have to do for now :)
int? val = 42; val.GetType() == typeof(int)
). So not only is nullable a struct that can be equal to null, it also often isn't a nullable at all! :D The same way, when you box a nullable value, you're boxing int
, not int?
- and when the int?
doesn't have a value, you get null
instead of a boxed nullable value. It basically means there's rarely any overhead from using nullable properly :)
Null
a type in .NET? Can you point to the part in the CLR/C# specification where that's said? Nullables are well defined in the CLR specification, their behaviour is no "implementation of an abstraction" - it's a contract. But if the best you can do is ad hominem attacks, enjoy yourself.
In VB.Net, do NOT use IsNot Nothing
when you can use .HasValue
. I just solved an "Operation could destabilize the runtime" Medium trust error by replacing IsNot Nothing
with .HasValue
in one spot. I don't really understand why, but something is happening differently in the compiler. I would assume that != null
in C# may have the same issue.
HasValue
because of readability. IsNot Nothing
is really an ugly expression (because of the double negation).
If you use linq and want to keep your code short, I recommand to always use !=null
And this is why:
Let imagine we have some class Foo
with a nullable double variable SomeDouble
public class Foo
{
public double? SomeDouble;
//some other properties
}
If somewhere in our code we want to get all Foo with a non null SomeDouble values from a collection of Foo (assuming some foos in the collection can be null too), we end up with at least three way to write our function (if we use C# 6) :
public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos)
{
return foos.Where(foo => foo?.SomeDouble != null);
return foos.Where(foo=>foo?.SomeDouble.HasValue); // compile time error
return foos.Where(foo=>foo?.SomeDouble.HasValue == true);
return foos.Where(foo=>foo != null && foo.SomeDouble.HasValue); //if we don't use C#6
}
And in this kind of situation I recommand to always go for the shorter one
foo?.SomeDouble.HasValue
is a compile-time error (not a "throw" in my terminology) in that context because its type is bool?
, not just bool
. (The .Where
method wants a Func<Foo, bool>
.) It is allowed to do (foo?.SomeDouble).HasValue
, of course, since that has type bool
. This is what your first line is "translated" into internally by the C# compiler (at least formally).
There second method will be many times more effective (mostly because of compilers inlining and boxing but still numbers are very expressive):
public static bool CheckObjectImpl(object o)
{
return o != null;
}
public static bool CheckNullableImpl<T>(T? o) where T: struct
{
return o.HasValue;
}
Benchmark test:
BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
[Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
Clr : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
Core : .NET Core 4.6.25009.03, 64bit RyuJIT
Method | Job | Runtime | Mean | Error | StdDev | Min | Max | Median | Rank | Gen 0 | Allocated |
-------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
CheckObject | Clr | Clr | 80.6416 ns | 1.1983 ns | 1.0622 ns | 79.5528 ns | 83.0417 ns | 80.1797 ns | 3 | 0.0060 | 24 B |
CheckNullable | Clr | Clr | 0.0029 ns | 0.0088 ns | 0.0082 ns | 0.0000 ns | 0.0315 ns | 0.0000 ns | 1 | - | 0 B |
CheckObject | Core | Core | 77.2614 ns | 0.5703 ns | 0.4763 ns | 76.4205 ns | 77.9400 ns | 77.3586 ns | 2 | 0.0060 | 24 B |
CheckNullable | Core | Core | 0.0007 ns | 0.0021 ns | 0.0016 ns | 0.0000 ns | 0.0054 ns | 0.0000 ns | 1 | - | 0 B |
Benchmark code:
public class BenchmarkNullableCheck
{
static int? x = (new Random()).Next();
public static bool CheckObjectImpl(object o)
{
return o != null;
}
public static bool CheckNullableImpl<T>(T? o) where T: struct
{
return o.HasValue;
}
[Benchmark]
public bool CheckObject()
{
return CheckObjectImpl(x);
}
[Benchmark]
public bool CheckNullable()
{
return CheckNullableImpl(x);
}
}
https://github.com/dotnet/BenchmarkDotNet was used
So if you have an option (e.g. writing custom serializers) to process Nullable in different pipeline than object
- and use their specific properties - do it and use Nullable specific properties. So from consistent thinking point of view HasValue
should be preferred. Consistent thinking can help you to write better code do not spending too much time in details.
PS. People say that advice "prefer HasValue because of consistent thinking" is not related and useless. Can you predict the performance of this?
public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
return t != null; // or t.HasValue?
}
PPS People continue minus, seems nobody tries to predict performance of CheckNullableGenericImpl
. I will tell you: there compiler will not help you replacing !=null
with HasValue
. HasValue
should be used directly if you are interested in performance.
CheckObjectImpl
boxes the nullable into an object
, whereas CheckNullableImpl
does not use boxing. Thus the comparison is very unfare. Not only it is not fare, it is also useless because, as noted in the accepted answer, the compiler rewrites !=
to HasValue
anyway.
Nullable<T>
, you do (by boxing it into an object
). When you apply != null
with a nullable on the left, no boxing occurs because the support for !=
for nullables works at the compiler level. It's different when you hide the nullable from the compiler by first boxing it into an object
. Neither CheckObjectImpl(object o)
nor your benchmark make sense in principle.
CheckObjectImpl
with its body inside CheckObject
. However your latest comments reveal that you in fact had a completely different question in mind when you decided to answer this 8 years old question, which makes your answer misleading in the context of the original question. It was not what the OP was asking about.
what is faster != or HasValue
. He arrives at this question, browses through your answer, appreciates your benchmark and says, "Gee, I will never use !=
because it's clearly so much slower!" That is a very wrong conclusion which he will then proceed spreading around. That is why I believe your answer is harmful - it answers a wrong question and thus plants a wrong conclusion in the unsuspecting reader. Consider what happens when you change your CheckNullableImpl
to also be return o != null;
You will get the same benchmark result.
!=
and HasValue
when in fact it shows the difference between object o
and T? o
. If you do what I suggested, that is, rewrite CheckNullableImpl
as public static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }
, you will end up with a benchmark that clearly shows that !=
is much slower than !=
. Which should lead you to conclusion that the issue your answer describes is not about !=
vs HasValue
at all.
Success story sharing
int? x = null
gives me the illusion that a nullable instance is a reference type. But the truth is that Nullable<T> is a value type. It feels I'd get a NullReferenceException to do:int? x = null; Use(x.HasValue)
.Nullable<int>
instead ofint?
.