Background:
With C# 8 and suitable settings, you distinguish nullable reference types from reference types "guaranteed" to be nonnull.
Targeting .NET Core, there is some support for this in the .NET library, for example if I say:
var valueOfEnvVar = Environment.GetEnvironmentVariable("NoVarWithThisName");
then valueOfEnvVar
will have type string?
, not just string
. This is because the library method GetEnvironmentVariable
will return null when the requested environment variable does not exist. Some "secret" attributes in the DLL (apparently netcoreapp3.1\System.Runtime.Extensions.dll in this example) make this work under the hood.
As every reader of this knows, the advantage here is if I say e.g. valueOfEnvVar.Contains("test")
, at compile-time I will get CS8602: Dereference of a possibly null reference. This prevents NullReferenceException
hell.
So far so good.
Now for the question. Suppose I have:
static void M(IEnumerable<string> seq)
{
var first = seq.FirstOrDefault(); // (1)
}
As you see, seq
is a (nonnullable) IEnumerable<>
of (nonnullable) strings. But there is a clear possibility it could be empty (if there was not, I would say .First()
, surely).
Still, the C# compiler says first
is nonnullable string
. If I go on to say first.Contains("test")
, the compiler will not stop or warn me. I am led directly into the run-time exception.
Why is this not as good as in the GetEnvironmentVariable
example?
Of course, I try to help to compiler and say:
static void M(IEnumerable<string> seq)
{
var first = (string?)seq.FirstOrDefault(); // (2)
}
or:
static void M(IEnumerable<string> seq)
{
string? first = seq.FirstOrDefault(); // (3)
}
But it does not really help. The compiler still thinks it is safe to do first.Contains
. No CS8602 for me.
In fact, Visual Studio development environment says my use of the type string?
is dubious and comes with helpful "potential fixes" that lead me back to the form (1)
which is wrong.
Why does this not work? (The thread Nullable reference types with generic return type seems to indicate that it should be technically possible to make this work even when FirstOrDefault
has a return type TSource
which at compile-time cannot be known to be a reference type.)