为什么必须显式声明嵌套类型?

Why the nested type must be explicitly declared?

我认为这是我第一次使用新的 C#6 字典初始化功能,我需要一个如下所示的嵌套结构:

        var map = new Dictionary<string, Dictionary<string, object>>()
        {
            ["WNodeScramblingResetZone_Unlock"] = 
            {
                ["updater"] ="pinco",
                ["rdlev"] = 55
            },
            ["WNodeScramblingResetZone_NewPwd"] = 
            {
                ["updater"] ="pallo",
                ["wrlev"] = 75,
                ["is_online"] = true
            }
        };

这个语法看起来不错:没有编译错误。但是,当程序启动时,我在输出 window 中看到几个 "KeyNotFoundException" 错误,应用程序无法运行。

顺便说一下,如果我显式声明嵌套类型实例化,一切运行正常。

        var map = new Dictionary<string, Dictionary<string, object>>()
        {
            ["WNodeScramblingResetZone_Unlock"] = new Dictionary<string, object>()
            {
                ["updater"] ="pinco",
                ["rdlev"] = 55
            },
            ["WNodeScramblingResetZone_NewPwd"] = new Dictionary<string, object>()
            {
                ["updater"] ="pallo",
                ["wrlev"] = 75,
                ["is_online"] = true
            }
        };

这是限制、错误,还是有具体的理由来定义它?

我使用 http://tryroslyn.azurewebsites.net/ 将此 C# 代码翻译成 VB.NET:

Public Class Program
    Public Shared Sub Main()
        Dim expr_06 As Dictionary(Of String, Dictionary(Of String, Object)) = New Dictionary(Of String, Dictionary(Of String, Object))()
        expr_06("x")("updater") = "pinco"
        expr_06("x")("rdlev") = 55
        expr_06("y")("updater") = "pallo"
        expr_06("y")("wrlev") = 75
        expr_06("y")("is_online") = True
    End Sub
End Class

它清楚地表明,新词典不是创建的,只能通过键访问。新的 [key] = value 语法编译成 dictionary[key] = value,不像旧的 {key, value} 编译成 dictionary.Add(key, value)

我认为这是一个特性,编译器不知道你需要什么类型的 IDictionary 所以你必须提供 concrete 类型。您还可以使用一些动态集合,在第一次访问时自动创建子词典,在这种情况下,此代码将起作用。正如您可能看到的那样,编译器以程序冗长为代价让您可以自由地做您想做的事。

编辑:我写的有点长 blog post about initializers 提供了更深入的解释

当你这样写代码时:

var map = new Dictionary<string, Dictionary<string, object>>()
{
    ["WNodeScramblingResetZone_Unlock"] = 
    {
        ["updater"] ="pinco"
    }
};

它被编译成等价物:

var map = new Dictionary<string, Dictionary<string, object>>()
map["WNodeScramblingResetZone_Unlock"]["updater"] = "pinco";

它不会创建您未明确指定的任何实例。

这是 可以 工作的代码(这就是它被允许的原因)对于一些类似字典的 class,returns 空集合的键不存在,但确实会导致 KeyNotFoundException for Dictionary.

另一方面,这段代码:

var map = new Dictionary<string, Dictionary<string, object>>()
{
    ["WNodeScramblingResetZone_Unlock"] = new Dictionary<string, object>()
    {
        ["updater"] ="pinco"
    }
};

编译为:

var map = new Dictionary<string, Dictionary<string, object>>();
var tmp = new Dictionary<string, object>();
tmp["updater"] = "pinco";
map["WNodeScramblingResetZone_Unlock"] = tmp;

如您所见,这会调用 map["WNodeScramblingResetZone_Unlock"] setter 一个新实例(而不是之前版本中的 getter ),这就是它起作用的原因。