Which is preferred: Nullable<>.HasValue or Null

2019-01-01 03:51发布

I always used (a) Nullable<>.HasValue because I liked the semantics. However, recently I was working on someone else's existing code base where they used (b) Nullable<> != null exclusively instead. Is there a reason to use one over the other, or is it purely preference?

(a)

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

(b)

int? b;
if (b != null)
    ...

6条回答
高级女魔头
2楼-- · 2019-01-01 03:59

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.

查看更多
宁负流年不负卿
3楼-- · 2019-01-01 04:03

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.

查看更多
旧人旧事旧时光
4楼-- · 2019-01-01 04:04

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

查看更多
大哥的爱人
5楼-- · 2019-01-01 04:04

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

查看更多
无色无味的生活
6楼-- · 2019-01-01 04:10

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

查看更多
还给你的自由
7楼-- · 2019-01-01 04:16

General answer and rule of thumb: 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. E.g. 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

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;
}

PPS People continue minus but nobody tries to predict performance of CheckNullableGenericImpl. And there compiler will not help you replacing !=null with HasValue. HasValue should be used directly if you are interested in performance.

查看更多
登录 后发表回答