将KeyValuePair枚举传递到FormUrlEncodedContent构造函数时,Visual Studio为什么会生成警告CS620?

问题描述

考虑以下代码

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);
        }
    }
}

在构建时调用FormUrlEncodedContent的构造函数会产生以下编译器警告:

警告 CS8620 不能使用类型'Dictionary '的参数 用于类型的参数“ nameValueCollection” 的'IEnumerable >' 'FormUrlEncodedContent.FormUrlEncodedContent(IEnumerable > nameValueCollection)”,因为可为空性存在差异 参考类型。

documentation for FormUrlEncodedContent指示构造函数应接受IEnumerable<keyvaluePair<string,string>>?。我的map变量为Dictionary<string,string>,大概会实现接口IEnumerable<keyvaluePair<string,string>>,所以我希望这里没有问题。 那为什么要警告?

我正在使用针对NETCore5的Visual Studio 16.8.0。

解决方法

我觉得这里实际上有几个问题:

  1. 为什么文档有误?而且,
  2. 为什么发出警告。

后者更容易回答。前者不是那么多(您必须问微软)。

我还没有安装.NET 5。但是我可以使用以下代码在.NET Core 3.1中重现完全相同的警告:

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

    M1(map);
}

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

有可能从您复制/粘贴到问题中的警告中推断出FormUrlEncodedContent类构造函数的.NET 5版本的 actual 声明必须是什么,所以我只写了一个不需要安装.NET 5的示例。

确实,如果我们查看the source code,我们可以看到该构造函数的实际声明:

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

这告诉我文档不正确,即使不是完全不正确的。我没有关注支持空值引用类型的框架的更改,因此我不确定文档是否期望在这种情况下显示空值类型参数。在我看来,应该,但显然不是。至少,令我惊讶的是文档显示nameValueCollection参数本身为可空值(实际上不是),没关系,当KeyValuePair<TKey,TValue>类型参数显示为不可空值时,实际上,它们是可以为空的。

但是,无论如何,通过了解.NET 5中的 actual 方法签名是什么,我们可以说警告出现在这里仅仅是因为,实际上,您的调用涉及传递与类型参数的可空性。

为什么这应该产生警告?好吧……虽然我们知道在这种情况下,没有代码将写入可枚举值中的可为空值,但没有编译时的保证。由于您的对象返回的对象中值是不可为空的引用,因此您将其传递给声明为可以接受可为空值的方法,这意味着从理论上讲,该方法可以修改通过将它们设置为null来实现可空值,这违反了您自己的代码对值不可为空的假设。