6

Why the code below doesn't work?

public T? Method<T>() 
{
    /*
     error CS0403: Cannot convert null to type parameter 'T' 
     because it could be a non-nullable value type.
     Consider using 'default(T)' instead.
    */
    return null;
}
W.H
  • 1,838
  • 10
  • 24
  • 3
    Does this answer your question? [Why cannot convert null to type parameter T in c#?](https://stackoverflow.com/questions/28133205/why-cannot-convert-null-to-type-parameter-t-in-c) – techie Sep 27 '21 at 21:39
  • Related [Nullable type as a generic parameter possible?](https://stackoverflow.com/q/209160/1441) – crashmstr Sep 27 '21 at 21:39
  • I closed this as a duplicate and reopened it. This question needs more clarification in modern C#. – TheGeneral Sep 27 '21 at 22:19

2 Answers2

7

For the sake of backward compatibility, T? is implemented as a pure compile-time construct. For reference type, it's the same as T at runtime. For value type, it is translated to Nullable<T>. The compiler needs to know for sure that T is a value type at compile time to do the translation. Thus for unconstraint type T, the compiler can't assume T? is nullable.

To get the behavior in the OP, the only OOTB way seems to be to overload the method for reference and value types. Unfortunately changing the type constraint is not enough for overload resolution so the name or the parameters would need to be different as well.

public static T? Method<T>() where T : struct
{
    return null;
}

public static T Method<T>(int placeholder = 0) where T : class
{
    return null;
}

Depending on what you're trying to do, Chenga's solutions are likely easier to work with, but this will work if you absolutely need it to return null with a non-nullable type constraint. If you're concerned about default being a non-null value you could also implement an Optional type such as the one in the question linked and return that instead of T.

W.H
  • 1,838
  • 10
  • 24
IllusiveBrian
  • 3,105
  • 2
  • 14
  • 17
  • 1
    JFYI _"T? is implemented as a pure compile-time construct"_ is not completely true, some metadata is emitted for NRTs which can be [processed at the runtime](https://stackoverflow.com/questions/75063872/how-to-know-if-the-type-is-string-or-string/75063993#75063993). – Guru Stron Feb 02 '23 at 13:01
  • 1
    @GuruStron That's interesting, and useful for debugging. I think what I was trying to express in this answer is that the IL type for NRTs is still the underlying reference type. [Example](https://sharplab.io/#v2:C4LglgNgPgAgTARgLACgYGYAE9MGFMDeqmJ2WuAFAJSHGn0DKwATmAHYDmmAbpgLyYARIIDcdeiSatOAfkwBjfpjYBXCBDEoJpdsDkBnJavWbtJXZmBKADKbMA1ADwwE1gHwVuVO9qcvrMh7y3uIS9hTBPmGOuoEU+iFaDvGJZpjhwKmkAL6hoRjYACzpjgAqHqWYYFRESdouAJwUYDIAdADiAKbApQCeAA6d1FkkuSjZQA=) – IllusiveBrian Feb 02 '23 at 17:46
  • Yes, I totally understand that, just a small addition which can be useful in some rare cases. – Guru Stron Feb 02 '23 at 17:49
  • How do you solve it for classes? say: public class Foo { public T? Bar() { return null;} } you can't overload classes ;-) or -.- – Ryanless May 30 '23 at 16:55
  • @Ryanless This is the best I can come up with - https://dotnetfiddle.net/od27xY As demonstrated it will have undesirable behavior if the struct extension is upcasted. – IllusiveBrian May 30 '23 at 21:53
  • @IllusiveBrian thank you for the code snippet. Though it makes it harder not easier to maintain a code base. For the moment I went with https://dotnetfiddle.net/33BFKR – Ryanless May 31 '23 at 11:35
-1

You have two options:

  1. return default(T) - my source is: https://www.c-sharpcorner.com/article/defaultt-in-generics/

  2. add a constraint: where T : class.

Chenga
  • 60
  • 2