C# 字典初始化编译不一致性

12
以下代码编译通过,但会出现“NullReferenceException”异常:
class Test
{
    public Dictionary<string, string> Dictionary { get; set; }
}

static void Main(string[] args)
{
    var x = new Test
    {
        Dictionary =   // fails
        {
            { "key", "value" }, { "key2", "value2" }
        }
    };
}

如果你用以下代码替换标记为'fails'的那行,它就可以正常工作:

Dictionary = new Dictionary<string, string> 

这个失败的语法有什么目的吗?它在其他情况下可以成功使用吗?还是编译器的一个疏忽?

3个回答

33

不,这不是一个错误... 这是您对初始化语法理解上的缺陷 :)

初始化语法的想法是

Dictionary = { ... }

用于调用方对集合属性具有读取访问权限但没有写入访问权限的情况。换句话说,就像这样的情况:

class Test
{
    private readonly Dictionary<string, string> dictionary 
        = new Dictionary<string, string>();
    public Dictionary<string, string> Dictionary { get { return dictionary; } }
}

基本上这就是对Add方法的调用,但没有先创建一个新的集合。所以这段代码:

Test test = new Test { Dictionary = { { "a", "b"}, {"c", "d" } };

等同于:

Test tmp = new Test();
Dictionary<string, string> tmpDictionary = tmp.Dictionary;
tmpDictionary.Add("a", "b");
tmpDictionary.Add("c", "d");
Test test = tmp;

这种方法有用的一个很好的例子是在 UI 的 Controls 集合中。你可以这样做:

Form form = new Form
{
    Controls = 
    {
        new Button { Text = "Hi" }, 
        new TextBox { Text = "There" } 
    }
};

但是你实际上不能设置Controls属性,因为它是只读的。


所以它被用于向构造函数创建的字典中添加项目 - 我应该意识到这一点。但这是等号运算符的奇怪用法,因为其效果是添加到字典中已有的任何内容(构造函数可能首先添加了项目)。 - Ben M
有点像,但同时它也用于设置集合中的初始值,因此在这方面它也很适合。 - Jon Skeet
正确。缺少的 new 应该是一个红色提示.. 但我从未使用过这个语法,所以我太字面地理解了等于号操作符。 - Ben M

4

你仍然可以在构造函数中使用你想要的语法:

Dictionary<string, string> dictionary = new Dictionary<string, string>
            {
                {"a", "b"},
                {"c", "d"}
            };

1

它因为你声明了一个未初始化的变量(Dictionary),所以会出现空引用异常。

当你尝试使用初始化器语法将条目添加到其中时,你正在尝试向一个空对象写入数据。

当你用“= new Dictionary…”替换该行时,你为Dictionary创建了一个新对象来引用,因此你随后可以成功地向其中添加条目。

(在Jon Skeet的示例中,控件集合必须已经由表单创建,因此它可以正常工作)


1
好的。Jon已经回答了你的问题,所以我想解释一下失败的原因,以防你没有理解。 - Jason Williams

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接