Why does Visual Studio generate warning CS620 when passing KeyValuePair enumeration to FormUrlEncodedContent constructor?

844 Views Asked by At

Consider the following code:

using System;
using System.Collections.Generic;
using System.Net.Http;

namespace dla.test2{
    internal class Test{
        public static void Main(){
            var map=new Dictionary<string,string>(){
                ["hello"]="world"
            };
            using var foo=new FormUrlEncodedContent(map);
        }
    }
}

Calling the constructor of FormUrlEncodedContent yields the following compiler warning at build-time:

Warning CS8620 Argument of type 'Dictionary<string, string>' cannot be used for parameter 'nameValueCollection' of type 'IEnumerable<KeyValuePair<string?, string?>>' in 'FormUrlEncodedContent.FormUrlEncodedContent(IEnumerable<KeyValuePair<string?, string?>> nameValueCollection)' due to differences in the nullability of reference types.

The documentation for FormUrlEncodedContent indicates the constructor should accept an IEnumerable<KeyValuePair<string,string>>?. My map variable, being a Dictionary<string,string>, would presumably implement the interface IEnumerable<KeyValuePair<string,string>> so I'd expect there to be no problem here. So why the warning?

I'm using Visual Studio 16.8.0 targeting NETCore5.

1

There are 1 best solutions below

2
Peter Duniho On BEST ANSWER

I feel like there are actually a couple of questions here:

  1. Why is the documentation wrong? And,
  2. Why is the warning emitted.

The latter is easier to answer. The former, not so much (you'd have to ask Microsoft).

I don't have .NET 5 installed yet. But I can reproduce the exact same warning in .NET Core 3.1 with this code:

static void Main(string[] args)
{
    var map = new Dictionary<string, string>()
    {
        ["hello"] = "world"
    };

    M1(map);
}

static void M1(IEnumerable<KeyValuePair<string?, string?>>? nameValueCollection) { }

It's possible to infer from the warning you copy/pasted into your question what the actual declaration of the .NET 5 version of the FormUrlEncodedContent class constructor must be, so I just wrote an example that doesn't require .NET 5 to be installed.

And indeed, if we look at the source code, we can see the actual declaration of that constructor:

public FormUrlEncodedContent(IEnumerable<KeyValuePair<string?, string?>> nameValueCollection)

This tells me that the documentation is inaccurate, if not outright incorrect. I haven't been following the changes to the framework for supporting nullable reference types, so I'm not sure if the documentation is expected to show nullable type parameters for this sort of situation. Seems to me it should, but clearly it's not. At the very least, it's surprising to me that the documentation shows the nameValueCollection parameter itself as nullable (which in fact it's not), never mind that it shows the KeyValuePair<TKey, TValue> type parameters as non-nullable, when in fact they are nullable.

But regardless, by understanding what the actual method signature is in .NET 5, we can then say that the warning is there simply because indeed, your call involves passing a type that is inconsistent with the nullability of the type parameters.

Why should this generate a warning? Well…while we know in this case no code will be writing to the nullable values in the values of the enumerable, there's no compile-time guarantee of this. Since your object is returning objects where the values are non-nullable references, and you pass this to a method that is declared as accepting values which are nullable, that means that in theory, that method could modify those nullable values by setting them to null, violating your own code's assumption that the values are non-nullable.