527

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?

  1. int? a;
    if (a.HasValue)
        // ...
    

vs.

  1. int? b;
    if (b != null)
        // ...
    
TylerH
  • 20,799
  • 66
  • 75
  • 101
lc.
  • 113,939
  • 20
  • 158
  • 187
  • 10
    I asked a similar question... got some good answers:http://stackoverflow.com/questions/633286/nullable-types-best-way-to-check-for-null-or-zero-in-c – nailitdown Mar 24 '09 at 03:32
  • 3
    *Personally*, I'd use `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. – Jake Petroules May 31 '10 at 13:16
  • 1
    `.HasValue` makes more sense as it denotes the type is of type `T?` rather than a type that can be nullable such as strings. – user3791372 Jun 11 '16 at 07:00

6 Answers6

579

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.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 92
    I would add to that "whichever is more consistent/follows an existing coding style." – Josh Lee Mar 24 '09 at 04:08
  • 22
    Wow. I hate this syntactic sugar. `int? x = null` gives me the illusion that a nullable instance is a reference type. But the truth is that Nullable is a value type. It feels I'd get a NullReferenceException to do: `int? x = null; Use(x.HasValue)`. – KFL Nov 04 '14 at 01:03
  • 15
    @KFL If the syntactic sugar bothers you, just use `Nullable` instead of `int?`. – Cole Tobin Mar 20 '15 at 23:08
  • 36
    In the early stages of creating an application you might think it's sufficent to use a nullable value type to store some data, only to realize after a while that you need a proper class for your purpose. Having written the original code to compare with null then has the advantage that you don't need to search/replace every call to HasValue() with a null comparison. – Anders Dec 16 '15 at 12:34
  • 26
    It's pretty silly to complain about being able to set a Nullable to null or compare it to null given that's **called Nullable**. The problem is that people are conflating "reference type" with "can be null", but that's a conceptual confusion. Future C# will have non-nullable reference types. – Jim Balter Nov 07 '16 at 23:33
  • 4
    @KFL I sometimes use `var z = x ?? y` for nullable types. Would you still prefer `var z = x.HasValue ? x : y` in that case? Seems kinda weird. – Nullius Jan 18 '17 at 12:10
  • @Nullius from KFL's example, it's the `= null` part that's kinda weird. If `null` were an instance of `Nullable`, so that you could expect `null.HasValue() == false` then it would be less weird that `x.HasValue()` works. – jpaugh May 17 '17 at 22:10
  • Is this also the case for Entity Framework LINQ expressions? – Kissaki Dec 08 '21 at 09:11
59

I prefer (a != null) so that the syntax matches reference types.

Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
cbp
  • 25,252
  • 29
  • 125
  • 205
  • 16
    Which is quite misleading, of course, since `Nullable<>` is *not* a reference type. – Luaan Aug 05 '15 at 09:22
  • 12
    Yes, but the fact usually matters very little at the point you are null checking. – cbp Aug 07 '15 at 04:41
  • 48
    It's only misleading to the conceptually confused. Using a consistent syntax for two different types does not imply that they are the same type. C# has nullable reference types (all reference types are currently nullable, but that will change in the future) and nullable value types. Using a consistent syntax for all nullable types makes sense. In no way does it imply that nullable value types are reference types, or that nullable reference types are value types. – Jim Balter Oct 11 '15 at 16:43
  • I prefer `HasValue` because it's more readable than `!= null` – Konrad Sep 21 '18 at 10:09
  • 1
    Coding consistance is more readable if you dont mix different styles of writing the same code. Since not all places have a .HasValue property then it makes since to use != null for increased consistancy. In my opinon. – ColacX Sep 24 '19 at 11:13
  • 2
    Definitely vote for this preference, if nothing else it makes coding changes simpler, as going from a reference type to a nullable type doesn't require code changes anywhere else, where using `.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. – Captain Prinny Feb 13 '20 at 19:17
  • In cases where you access `.Value` it makes sense to use `.HasValue` though. – Kissaki Dec 08 '21 at 09:12
  • What happens if anybody creates a custom equality operator (== or !=)? I could write my own operator so that `var a = new A(); a == null` is `true`. Not that any sane person would do that but it is possible. `HasValue` doesn't run custom equality operators as far as I'm aware. – kkuilla Apr 04 '23 at 17:53
  • In that case, you find the person who did it and get them the help they need. – cbp Apr 06 '23 at 08:57
23

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? :)

Perrin Larson
  • 562
  • 4
  • 14
  • 9
    "Keep in mind: Nullable or the shorthand something? is a class." This is wrong! Nullable is a struct. It overloads Equals and == operator to return true when compared to null. The compiler does no fancy work for this comparison. – andrewjsaid Jun 09 '15 at 09:30
  • 1
    @andrewjs - You are right that it's a struct (not a class), but you are wrong that it overloads the == operator. If you type `Nullable` 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 :) – Perrin Larson Jun 11 '15 at 20:15
  • I did a quick check through ildasm and you are right about the compiler doing some magic; comparing a Nullable object to null does in fact translate to a call to HasValue. Interesting! – andrewjsaid Jun 12 '15 at 10:39
  • 3
    @andrewjs Actually, the compiler does a ton of work to optimize nullables. For example, if you assign a value to a nullable type, it will not actually be a nullable at all (e.g., `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 :) – Luaan Aug 05 '15 at 09:26
  • "Without help from the compiler, trying to call HasValue on a Nullable whose variable was assigned null would produce a null reference runtime error." -- This is nonsense. Nullable is a value type, not a reference type, and so it can't possibly get a null reference error. A number of comments on this page reflect this confusion, conflating "nullable" with "reference type". – Jim Balter Oct 11 '15 at 16:50
  • @Luaan "it will not actually be a nullable at all" -- this and the rest of your comment is nonsense. Types are nullable; values aren't. A nullable type T? can be either a T or null. Of course when it's a T, GetType() returns typeof(T), because GetType yields the *runtime* type. "there's rarely any overhead" -- of course there is overhead; extra storage is needed for the flag that says whether the value is null, and runtime checks are required to test that flag. – Jim Balter Nov 07 '16 at 23:27
  • @JimBalter Okay, so tell me how a *value type* can have a different *runtime* type from a compile-time type without the runtime/compiler cheating. Can you make your own nullable type that would behave the same way? Can you overload the boxing operator for a value type? How do you overload `GetType`? What overhead do you get from `Nullable.GetValueOrDefault`? What overhead do you get from boxing a nullable type on top of the boxing of any other value type? The only case where you get overhead is when not boxing (the flag) and when checking for null (that's what you wanted!). – Luaan Nov 08 '16 at 08:53
  • @Luaan A variable with `int?` compile-time type has a runtime type of either `int` or `Null`. It isn't "cheating", it's an *implementation* of an *abstraction*. A variable with `Base` compile-time type can have `Derived` as runtime type -- no "cheating". This is very basic. – Jim Balter Nov 10 '16 at 08:09
  • 1
    @JimBalter Really? That's very interesting. So what does the memory profiler tell you about a nullable field in a class? How do you declare a value type that inherits from another value type in C#? How do you declare your own nullable type that behaves the same as .NET's nullable type? Since when is `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. – Luaan Nov 10 '16 at 08:51
13

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.

Yifan Ai
  • 116
  • 1
  • 2
  • 10
Carter Medlin
  • 11,857
  • 5
  • 62
  • 68
  • 10
    I would prefer `HasValue` because of readability. `IsNot Nothing` is really an ugly expression (because of the double negation). – Stefan Steinegger Jul 08 '13 at 09:05
  • 12
    @steffan "IsNot Nothing" isn't double negation. "Nothing" isn't a negative, it's a discrete quantity, even outside the realm of programming. "This quantity is not nothing." is, grammatically, the exact same as saying "This quantity is not zero." and neither is a double negative. – jmbpiano Mar 05 '15 at 23:49
  • 8
    It's not that I don't want to disagree with the absence of truth here, but come on now. IsNot Nothing is clearly, well, overly negative. Why not write something positive and clear like HasValue? This is not a grammar test, it's coding, where the key objective is clarity. – Randy Gamage Aug 12 '15 at 22:22
  • 5
    jmbpiano: I agree it's not double negation, but it's a single negation and that's almost as ugly and not as clear as a simple positive expression. – Kaveh Hadjari Jan 05 '16 at 18:14
0

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

yan yankelevich
  • 885
  • 11
  • 25
  • 2
    Yes, `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`.) 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). – Jeppe Stig Nielsen Jul 07 '18 at 15:08
-8

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.

Roman Pokrovskij
  • 9,449
  • 21
  • 87
  • 142
  • 4
    Your `CheckObjectImpl` [boxes](http://stackoverflow.com/q/2111857/11683) 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](http://stackoverflow.com/a/676089/11683), the compiler rewrites `!=` to `HasValue` anyway. – GSerg May 16 '17 at 10:06
  • GSerg your comment is unfare. I can only repeat that it is specific situation, but it is common case in some areas e.g. in serializers. Additionally to answer I demonstrate how things can get more complicated if you ignore struct nature of Nullable. Readers will decide is it useless or not, for me it is intersting and I share it. – Roman Pokrovskij May 16 '17 at 11:37
  • 3
    The readers do not ignore struct nature of `Nullable`, *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. – GSerg May 16 '17 at 11:46
  • 4
    My problem is that I care about content quality on this website. What you posted is either misleading or wrong. If you were attempting to answer the OP's question, then your answer is flat out wrong, which is easy to prove by replacing the *call* to `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. – GSerg May 16 '17 at 12:16
  • Yes I understand that my answer is more generic and the same time more specific than question and showing situation from another angle. And I agree your comment have sense. Except two moments: a) situation is real b) you want not only comment but delete my answer. Second I can explain only with bublegums that you expect to get for it. Shame on you. – Roman Pokrovskij May 16 '17 at 12:29
  • 1
    I strongly believe that this website is as successful as it is specifically because it's built on the principle of narrow specific questions with narrow specific answers, which is why I believe it was correct to vote to delete this content *from this question*. It's very welcome if you have content to share, just do it under a more appropriate question. If there isn't one, it's [absolutely fine](https://meta.stackexchange.com/q/17463/147640) to create a question and answer it yourself. – GSerg May 16 '17 at 12:39
  • 4
    Put yourself in the shoes of the next guy who googles `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. – GSerg May 16 '17 at 14:55
  • You are arguing not with my answer. My answer is for concrete specific situation. I do not hide that this situation is specific, describe the situation with text ("serializers pipelines"), show code and interesting numbers. People are not stupid, can understand it, start thinking and enjoy theirs ideas. Thinking is joyful, not "badge hunting". – Roman Pokrovskij May 16 '17 at 15:33
  • 10
    I am arguing with your answer. Your answer deceptively looks like it shows the difference between `!=` 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? 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. – GSerg May 16 '17 at 16:04
  • 2
    Having realized that, you could reword your question to explain that the difference is in fact between `object o` and `T? o`, regardless of whether one uses `!=` or `HasValue`, so one should avoid boxing and unboxing nullable values when writing custom serializes. Then it would be an absolutely correct and not in the slightest misleading answer, but, after you would have re-read it, you would have deleted it because it would have been completely irrelevant to what the OP asked. – GSerg May 16 '17 at 16:04