Search code examples
c#c#-8.0collection-initializernullable-reference-types

Use of C# 8 null-forgiving operator within C# 6 index initializers


When using an index initializer to fill a dictionary without recreating it before that, is there an option to specify the null-forgiving operator somewhere?

Example:

public class Program
{
    public IDictionary<string, string>? NullableDictionary { get; set; }
       = new Dictionary<string, string>();

    public static void Main(string[] args)
    {
        new Program
        {
            NullableDictionary =
            {
                ["key"] = "value"
            }
        };
    }
}

I know that this use case (without recreating a new Dictionary<string, string> before index initialization { [...] = ... }) is probably not very common. But I am still wondering whether there is a solution to prevent this compiler warning for the above case:

[CS8602] Dereference of a possibly null reference.

I could imagine using the null-forgiving operator like this:

new Program
{
    NullableDictionary! =
    {
        ["key"] = "value"
    }
};

Solution

  • I am still wondering whether there is a solution to prevent this compiler warning

    I assume that you mean, other than the obvious #pragma approach:

    #pragma warning disable 8602
        new Program
        {
            NullableDictionary = { ["key"] = "value" }
        };
    #pragma warning restore
    

    That obviously works. Or, of course, the "nullable reference types"-specific version of the above:

    #nullable disable
        new Program
        {
            NullableDictionary = { ["key"] = "value" }
        };
    #nullable restore
    

    Both of these temporarily and explicitly disable the warning in question. The null-forgiving operator does essentially this, except of course for just the specific expression it's applied to.

    For better or worse, I don't think there's a way to do exactly what you want. Normally the collection-initializer syntax is allowed only on actual variable declarations. The use of it in the object-initializer syntax context is a special case the language allows, based on the similarity of the scenarios, even though the generated code is very different from an actual variable initialization (and in particular, where you'd in fact be initializing the variable's value at the same time as you initialize the collection).

    So, by the time the compiler is dealing with your collection initializer syntax, all that the compiler has to go on is that it's generating a bunch of calls to the Add() method, and that these calls are being made to a reference value that might be null, according to your earlier declaration.

    All that said, I find your attempt to use the null-forgiving operator there quite sensible. It would be nice if the compiler would interpret the use of the operator in that context by including that operator in each Add() call that it generates.

    You might even be able to file a Git issue against the language and ask that that be added as a feature. It wouldn't be a breaking change, I think, since that syntax is currently just plain illegal, and allowing it wouldn't change the behavior of the compiler when you're not using that syntax.