3

I have an unexpected behaviour with nullables on primitives.

My test code:

Nullable<long> value = long.Parse("5");
Type type = value.GetType();
// at this Point type is System.Int64 and not Nullable<System.Int64>

Is there any possibility, where value stays a Nullable<System.Int64> and does not get converted to a regular long?

Arndt Bieberstein
  • 1,128
  • 1
  • 14
  • 28
  • `long.Parse` returns a `long`, not a `long?` - allways. So surely the answer is: no. Why you even use `long?`? – MakePeaceGreatAgain Jan 24 '21 at 14:01
  • well, `long.Parse` returns a `long` and not a `long?` - so the actual content of the variable _is_ `long`. that being said: if you value _were_ `null`, you couldn't call `.GetType()` on it, anyway. – Franz Gleichmann Jan 24 '21 at 14:02
  • Well value is strongly typed as Nullable. I would expect to use a Nullable after the declaration and not a long. – Arndt Bieberstein Jan 24 '21 at 14:06
  • Check [this](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types#how-to-identify-a-nullable-value-type) documentation to read *How to identify a nullable value type*. – user1672994 Jan 24 '21 at 14:20

2 Answers2

3

No. You have already assigned it to a long? variable.

When you call GetType(), this function is defined on System.Object. Therefore to call it, we must box the value.

You can see this by viewing the generated MSIL instructions for the GetType() call:
box System.Nullable<System.Int64>
call System.Object.GetType
So when it is boxed it actually pushes the long? on the stack, and the boxing only gives us a boxed long

Hence the resulting value, in the case of a nullable, is the base type (see here and here).

In this instance, there is no reason to call GetType anyway, because we know it's a long?

Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • 1
    _When you call GetType(), this function is defined on System.Object. Therefore to call it, we must box the value._ this is non-intuitive, and even if I comprehend it perfectly, and I comprehend why it happens, still it remains non-intuitive :-) – xanatos Jan 24 '21 at 14:10
  • 100% agreed, `Nullable<>` (and valuetype in general) semantics leave what to be desired – Charlieface Jan 24 '21 at 14:12
  • 1
    "because we know it's a long?" No, it´s **not** a `long?` - at least not at runtime, which `GetType` will cover. – MakePeaceGreatAgain Jan 24 '21 at 14:14
  • 1
    @HimBromBeere It is a `long?` after assignment to a `long?` variable. It is then boxed **afterwards**, converting it back to a boxed `long` – Charlieface Jan 24 '21 at 14:16
  • `5` is not `long?`, it is **assignable** to it, though. So no, the value is still a `long`. There´s a huge difference between how you **declare** a variable and what it **contains** at runtime - and *that's what `GetType` will cover. – MakePeaceGreatAgain Jan 24 '21 at 14:17
  • **Rad my answer**, boxing converts it **back** into a `long`. The variable is 100% a `long?`. See this [fiddle](https://dotnetfiddle.net/jXkNLC) – Charlieface Jan 24 '21 at 14:27
  • boxing converts to long? Eeehm, no... that is **un-boxing**. Btw: your fiddle doesn´t handle `GetType`. – MakePeaceGreatAgain Jan 24 '21 at 14:30
  • See [the docs here](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types#boxing-and-unboxing). You can also see the IL generated for `((long?)5).GetType()` is `newobj System.Nullable..ctor box System.Nullable call System.Object.GetType` so it is definitely a `long?` on the stack until the `box` instruction – Charlieface Jan 24 '21 at 14:37
0

When you look at the docs long.Parse returns long, not long?.

public static long Parse (string s);

You can of course convert that to a long?, but that doesn´t change that statement.

long a = long.Parse("5");
Nullable<long> n = a; // convert to long?

So in short there´s no way long.Parse will ever return something different than a long. Even when you provide some invalid input:

long a = long.Parse("Hello"); //throws FormatException

Calling GetType on your variable will give you the runtime-type, not the compile-time one. As Parse returns a long at runtime, GetType surely does also.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111