I'm converting some old C# code to use C# 8, and have encountered this problem:
class MyList<T> : IMyEnumberable<T>
{
public T GetDefault()
{
return default;// a default expression introduces a null value when 'T' is a non-nullable reference type
}
}
It failed to compile and says
a default expression introduces a null value when 'T' is a non-nullable reference type
I'm not sure what is the problem here - any explanations?
The problem is that someone could do:
var list = new MyList<string>();
string d = list.GetDefault();
Because they created a MyList<string>
, then T
is string
, (i.e. a non-nullable string). Therefore because GetDefault()
returns a T
, this should mean that it returns a non-nullable string.
However if they call GetDefault()
, that will return default(string)
, which is null
. They'll get null
when they weren't expecting one!
You can't prevent someone from creating a MyList<string>
: there's no syntax to say "T
must only be a nullable type, and must not be a non-nullable type".
If you constrain T
to be a struct
or class
, you can write:
class MyList<T> : IMyEnumberable<T> where T : struct // or : class
{
public T? GetDefault()
{
return default;
}
}
Alternatively, you can add a [MaybeNull]
to the return type of GetDefault()
to say that, even though it returns T
(and T
may be non-nullable), this method might actually return null
.
(Note that currently this only affects callers of GetDefault()
, and not the actual body, so you'll still need the null-suppressing operator !
. It looks like this will change soon):
using System.Diagnostics.CodeAnalysis;
class MyList<T> : IMyEnumberable<T>
{
[return: MaybeNull]
public T GetDefault()
{
return default!;
}
}