T?, nullable type parameter in C# 9
Asked Answered
L

2

9

This program has two errors:

using System;

T? f<T>(T? t)
{    
    t = null; //  Error CS0403  Cannot convert null to type parameter 'T' because it could be a non-nullable value type
    return default(T?);
}

if(f(10) is null) // Error  CS0037  Cannot convert null to 'int' because it is a non-nullable value type    
    Console.WriteLine("null");

T? has to be a nullable type. But it seems T? is the same as T in the above program.

Is ? ignored in T??


Edit: Both errors disappear with a struct constraint:

using System;

T? f<T>(T? t)  where T : struct
{    
    t = null; // error
    return default(T?);
}

if(f<int>(10) is null) // error
    Console.WriteLine("null");

I don't understand why the constraint changes the results.

Longshoreman answered 17/1, 2021 at 20:5 Comment(4)
The C# language development team decided that it's a good idea for T? to just mean "defaultable" in the unconstrained case, not "nullable". I think that was a really stupid move as it leads to exactly the confusion you're in right now. But yeah: in C#, T? doesn't always mean nullable.Avon
Subscribing to @Avon 's comment that this is a very counterintuitive aspect of the language.Garmon
@Avon You say T? means just "defaultable". But then, int (which is defaultable, default(int)=0) would be int?, which is obviously not true. Can you be more specific regarding "in C#, T? doesn't always mean nullable"? Do you have an example for T where T? can NOT be null?Issie
That is precisely the point of my dislike of what the C# language team decided there, @Kjara.Avon
I
7

When you say T? in T? and in (T? t), they both refer to nullable reference types, not to the special Nullable<T> struct. There's no way that you can specify a generic parameter such that you can treat it as a class and a nullable value type.

The second error is just because f(10) (so f<int>(10)) is implicitly taken as an int (as there's no such thing as a nullable reference int value), so null isn't valid, just as if you did if (10 is null).


If T stops being open and instead you add a constraint such as where T : struct, T? becomes System.Nullable<T> rather than a nullable reference parameter, and so the code becomes the exact same as before nullable reference types were introduced.

Impost answered 17/1, 2021 at 20:16 Comment(4)
@MinimusHeximus That happens because now T? is a Nullable<T> where T: struct, but you can no longer pass reference types to the methodImpost
So it seemsT? is regarded as T when T is a value type and as T? when T is a reference type. Am I correct?Longshoreman
@MinimusHeximus Yes, that's correct, if you're referring to the original code and not the updateImpost
@CamiloTerevinto is this an inherent restriction that cannot be worked with or a design choice? Let's say I have a generic entity, where the generic type denotes a foreign key type. Currently, I either have to lock down the type to value/reference types or get an odd behavior (string?, but long, etc). Before C# 9 we couldn't even use T? without constraints, which felt better, since the current behavior seems very unintuitive, to me, at least.Jaggy
P
1

I don't have an idea for the first error, but the second.

f(10) is null is inferred as int in lieu of int? since 10 is of int type. Either f((int?)10) is null, or f<int?>(10) is null should be used.

Prudhoe answered 17/1, 2021 at 20:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.