1

Given the following code samples:

class Value { }

Value? value = null;

if (value is not null)
{
    Value value2 = value;
}
int? value = null;

if (value is not null)
{
    int value2 = value; // CS0266: Cannot implicitly convert type 'int?' to 'int'.
}

I understand the issue has to do with reference types vs value types.

However, in both cases I am checking whether value is null, so why can the compiler not understand that value should never be null inside the if block in the second example?

I can remedy the issue by using value.Value, but this seems counterintuitive.

Help would be appreciated.

katkak
  • 23
  • 4
  • 1
    Quite simply because the types don't match. – phuzi Aug 22 '23 at 11:57
  • 1
    Value types use `INullable`, and this is distinct from nullable reference types. The compiler does not treat them the same way, and this can lead to unexpected behaviors like this. So while I agree that his is counterintuitive, I do not think there is any good solution other than using `value.Value`. – JonasH Aug 22 '23 at 12:00
  • You null check it, but as @phuzi states the type is still Nullable. You would have to get the value `value.GetValueOrDefault()`. – Roe Aug 22 '23 at 12:01
  • Why don't you simply assign value to `value2` like this: `int value2 = value.HasValue ? value.Value : default;` or in more concise format: `int value2 = value ?? default;` – Peter Csala Aug 22 '23 at 12:14
  • @PeterCsala The samples are minimal reproductions of the actual code I was writing. The original code involved nullable DateTimes and adding them to a dictionary if they are not null. – katkak Aug 22 '23 at 12:22
  • @JonasH - `INullable` you meant `Nullable` I assume? – Rand Random Aug 22 '23 at 12:24
  • 1
    @RandRandom Yes, exactly. – JonasH Aug 22 '23 at 12:48

1 Answers1

4

Because nullable value types in C#/.NET are represented via a separate type (Nullable<T>), i.e. int? is actually is Nullable<int>. You can use pattern matching. For example:

if (value is {} value2)
{
    Console.WriteLine(value2);
}

Or

if (value is int value2)
{
    Console.WriteLine(value2);
}

Or just access the Nullable.Value:

if (value is not null)
{
    int value2 = value.Value;
}

Nullable references types on the other hand are not represented via a separate type (i.e. string? from the type info point of view is string and nullability information is stored in some kind of metadata - see for example this answer), i.e. one could argue that some kind of type erasure is happening in this case.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132