Why Does Visual Studio Type a Newly Minted Array as Nullable

Why does Visual Studio Type a Newly Minted Array as Nullable?

Visual Studio with C# 8 allows you to use nullable types according to the contexts you setup in your project. You can find docs Here.

One way to enable it is with a <Nullable>enable</Nullable> entry in your project file. If you have that then it'll choose to use the nullable type when you convert to explicit variable.

I'm not sure if that same behavior would be used for the other ways - pragmas for example - to enable it. I only tried the project file method.

Non-nullable reference type: why is my object considered nullable by the compiler?

In the original implementation, foo would have been inferred as a Foo.

However, people complained that this got in the way of things like:

string? GetThing() => ...

var result = "";
if (condition)
{
result = GetThing();
}

If result is inferred as a string, then the result = GetThing() line causes a warning: GetThing() returns a string?, and there's a warning if you try and assign a string? to a string.

The solution was to infer result as a string?, but the compiler knows that it's currently not null (its "flow state" is "NotNull").

This means that:

string? GetThing() => ...

var result = "";

// No warning, as the compiler knows that result isn't null
int l1 = result.Length;

if (condition)
{
result = GetThing();
}

// Warning: the compiler knows 'result' might have been re-assigned
int l2 = result.Length;

For other examples of the flow state at work, see things like:

string? result = GetString();
if (result == null)
throw new Exception();

// No warning: the compiler knows that result can't be null here: if it was,
// the exception above would have been thrown
int l1 = result.Length;
string? result = GetString();

// Warning: result might be null
int l1 = result.Length;

// No warning: the compiler knows that result can't be null here: if it was,
// the line above would have thrown
int l2 = result.Length;
string result = "hello";
if (result == null)
Console.WriteLine("NULL!");

// Warning: because we checked for null above, the compiler assumes that we
// know something that it doesn't, and so result might be null.
int l1 = result.Length;

In C# 8, why does type inference on new expressions result in nullable references?

If var were to infer its nullability from the expression, then in many instances you would not be able to assign a null to it later on. For example, var s = "";.
There was a discussion to allow var? to express "the nullable version of the type", but it had several issues. Would the regular var be restricted to infer a non-nullable type?

If yes, then (1) we're creating adoption pain as users need to add more ? annotations, (2) we have an inconsistent nullability with var pattern which had already shipped, (3) there are some questions with nullable value types (int?).
If no, then the intent of the code would not be very clear. var? would clearly indicate a nullable type, but var would be a mixed bag of nullable and non-nullable.

The decision to infer a nullable type for var was recorded in the LDM notes from 2019-12-18.

Why does async/await in C# return nullable values even when told not to?

This is by-design and does not have to do with the await.

var is always nullable, from the spec:

var infers an annotated type for reference types. For instance, in var s = ""; the var is inferred as string?.

As such, you can't explicitly specify theString as non-nullable when you use var. If you want it to be non-nullable, use string explicitly.


As to why: In short, it's to allow scenarios like this:

#nullable enable

public async Task<string> GetStringAsync(); ...

public async void Main()
{
var theString = await GetStringAsync();

if (someCondition)
{
// If var inferred "string" instead of "string?" the following line would cause
// warning CS8600: Converting null literal or possible null value to non-nullable type.
theString = null;
}
}

The compiler will use flow analysis to determine whether or not the variable is null at any given point. You can read more about the decision here.

Can this value really ever be null, or is the C# compiler just confused by code?

Probably the code analyzer is not sufficiently intelligent to conclude your date and time can never be null.

You can inform it of that fact by using the 'null forgiving' operator:

public static DateTime JoinNullableDateTime(DateTime? date, DateTime? time)
{
...
else
return date!.Value.Date.Add(time!.Value.TimeOfDay);
}

Why isn't a type constraint of where T : object compiling in C# 8

This is a valid question and the answer is not trivial. Things have changed a lot since the initial proposal, for valid reasons. Using the object? syntax would lead to confusion in other places.

The link points to the proposal document, not the actual documentation or specification. You'll find the documentation in the C# Guide's Nullable Reference Types. There's no referecence to generic constraints there. The Constraints on type parameters article doesn't mention object? either, but it does mention the notnull constaint.

The changes and the reasons behind them are explained in Try out Nullable Reference Types.

The common case: Nulls allowed

This :

public class A<T>
{
T DoStuff(T input)
{
return input;
}
}

Accepts any struct or value type, including null types. The following line doesn't generate any warnings :

var x=new A<string?>();

notnull constraint

You have to specify that you want non-nullable types with the notnull constraint :


public class A<T>
where T:notnull
{
T DoStuff(T input)
{
return input;
}
}

Using string? as a type parameter creates a warning now:

warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'string?' doesn't match 'notnull' constraint.

The problem with T?

To use nullable types you have to specify whether the type is a class or struct. The reason for this is explained in The issue with T? section of the blog post that introduced NRTs. T? implies that T is non-nullable so what is T? Class or Struct? The compiler handles each case differently. With a struct, the compiler will generate a Nullable<T> type while classes are handled by compiler magic.

This code :

public class A<T>
{
T? DoStuff(T input)
{
return input;
}
}

Will throw a compiler error, not just a warning :

A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.

Adding the class constraint and passing string as the type parameter doesn't generate any errors or warnings :

public class A<T>
where T:class
{
T? DoStuff(T input)
{
return input;
}
}

var x=new A<string>();


Related Topics



Leave a reply



Submit