1

If I have a nullable value type I always have to use its Value property, even after I checked it for null. Is there a consise way around it?

public void Foo(SomeStruct? s)
{
    if (s != null)
    {
        DoIt(s.Value.x + s.Value.y + s.Value.z);
    }
}

The obvious way would be to define a new variable which makes the code longer for each variable it affects and which I find makes the code harder to read:

    if (s != null)
    {
        var sv = s.Value;
        DoIt(sv.x + sv.y + sv.z);
    }

One other thing that came to my mind is pattern matching, but this has the drawback of a runtime typecheck:

    if (s is SomeStruct sv)
    {
        DoIt(sv.x + sv.y + sv.z);
    }

Am I overlooking something (besides the fact that I maybe should avoid null variables in the first place)?

codymanix
  • 28,510
  • 21
  • 92
  • 151

2 Answers2

2

If C# 8 is available to you - you can use property pattern which actually is turned into HasValue check:

    if(s is {} sv)
    {
        Console.WriteLine(sv);
    }

Will be turned by compiler into something like this (sharplab):

    int num;
    if (s.HasValue)
    {
        valueOrDefault = s.GetValueOrDefault();
        num = 1;
    }
    else
    {
        num = 0;
    }
    if (num != 0)
    {
        Console.WriteLine(valueOrDefault);
    }
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • You might make a note of the version restrictions on the code you are showing. A lot of us are using older versions of the compiler. It's easy to upgrade Visual Studio, it take longer to get the company build system upgraded. – Flydog57 Apr 21 '21 at 19:46
  • Although the generated code looks inefficient at first glance, I checked that in release configuration it indeed generates the same code that I would get when I'd done a normal null-check. – codymanix Apr 21 '21 at 20:09
  • The syntax is somewhat ugly and unintuitive..but it is nevertheless exactly what I was looking for :) – codymanix Apr 21 '21 at 20:12
2

if (s is SomeStruct sv) does not result in a runtime type-check. It's just a straight HasValue check

See this on Sharplab for instance:

int? x=5;
if (x is int xv)
{
    xv.ToString();
}

This compiles down to the equivalent of:

int? x=5;
int xv;
if (x.HasValue)
{
    xv = x.GetValueOrDefault();
    xv.ToString();
}

Note that GetValueOrDefault is fully optimized and does not check the bool HasValue flag.

The actual IL is as follows

        IL_0000: ldloca.s 0
        IL_0002: ldc.i4.5
        IL_0003: call instance void valuetype [System.Private.CoreLib]System.Nullable`1<int32>::.ctor(!0)
        IL_0008: ldloca.s 0
        IL_000a: call instance bool valuetype [System.Private.CoreLib]System.Nullable`1<int32>::get_HasValue()
        IL_000f: brfalse.s IL_0021

        IL_0011: ldloca.s 0
        IL_0013: call instance !0 valuetype [System.Private.CoreLib]System.Nullable`1<int32>::GetValueOrDefault()
        IL_0018: stloc.1
        IL_0019: ldloca.s 1
        IL_001b: call instance string [System.Private.CoreLib]System.Int32::ToString()
        IL_0020: pop
Charlieface
  • 52,284
  • 6
  • 19
  • 43