【问题标题】:Why does Visual Studio generate warning CS620 when passing KeyValuePair enumeration to FormUrlEncodedContent constructor?为什么将 KeyValuePair 枚举传递给 FormUrlEncodedContent 构造函数时,Visual Studio 会生成警告 CS620?
【发布时间】:2020-10-22 22:56:38
【问题描述】:

考虑以下代码:

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&lt;KeyValuePair&lt;string,string&gt;&gt;?。我的map 变量是Dictionary&lt;string,string&gt;,大概会实现接口IEnumerable&lt;KeyValuePair&lt;string,string&gt;&gt;,所以我希望这里没有问题。 那么为什么会出现警告?

我正在使用面向 NETCore5 的 Visual Studio 16.8.0。

【问题讨论】:

  • 因为string != string?(例如“由于引用类型的可空性不同”)。
  • 但是当一个可以为空的字符串(字符串?)被期望时,不能像我在这里一样,总是作为参数提交一个非空字符串吗?我理解“字符串?”表示字符串可以为空或非空,但也许我误解了。
  • "不能是非空字符串...总是在需要可空字符串(字符串?)时作为参数提交?" -- 不能作为类型参数,没有。就此而言,它也不允许(没有警告)作为引用参数。您需要记住,类型的这些位置涉及输入和输出;保证您不会传递空值是不够的……如果被调用的方法最终提供了一个空值,您还需要保证您可以接受一个空值。请参阅下面的解释。
  • 请注意,泛型类型差异正确地遵守了可空性规则。 IE。 IEnumerable&lt;string?&gt; 参数可以接受IEnumerable&lt;string&gt;,因为协变类型参数承诺提供的值只是输出,而不是输入,同样IEnumerable&lt;string&gt; 参数不会接受IEnumerable&lt;string?&gt;,因为a后者可能会返回空值,而前者只需要非空值。这也适用于逆变类型参数(即,...&lt;in T&gt; 用于接口声明),但当然是另一个方向。

标签: c# syntax compiler-warnings .net-5


【解决方案1】:

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

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

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

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

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

这告诉我文档不准确,如果不是完全不正确的话。我没有关注支持可为空引用类型的框架的更改,因此我不确定文档是否应该针对这种情况显示可为空的类型参数。在我看来它应该,但显然不是。至少,令我惊讶的是,文档显示nameValueCollection 参数本身为可空(实际上不是),更不用说它显示KeyValuePair&lt;TKey, TValue&gt; 类型参数不可为空,而实际上它们是可以为空。

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

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

【讨论】:

  • 我明白你在说什么,但 KeyValuePair 位不是暗示可以使用空键提交内容吗?这在地图上是不可能的吗?一个我能理解的空值,但一个空键?
  • 对空键的禁止来自字典对象本身;它不是 KeyValuePair&lt;TKey, TValue&gt; 所固有的。 FormUrlEncodedContent 对象没有反映该禁令;即它不介意您是否为键/值对传递空键。所以没有理由为KeyValuePair&lt;...&gt; 中的键类型参数指定不可为空的字符串。因为你是使用字典来初始化集合,所以不能提供null,但是还有其他方法可以提供IEnumerable&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;
猜你喜欢
  • 2020-09-30
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
相关资源
最近更新 更多