集合初始化式语法的区别

本文关键字:区别 语法 初始化 集合 | 更新日期: 2023-09-27 18:03:32

MSDN声明:

通过使用集合初始化器,您不必在源代码中指定对类的Add方法的多次调用;编译器添加调用。

他们也给出了这个例子,使用了新的集合初始化语法,括号:

var numbers = new Dictionary<int, string> { 
    [7] = "seven", 
    [9] = "nine", 
    [13] = "thirteen" 
};

然而,当检查生成的IL代码时,似乎这段代码根本没有导致对Add方法的任何调用,而是对一个set_item的调用,如下所示:

IL_0007: ldstr        "seven"
IL_000c: callvirt     instance void class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::set_Item(!0/*int32*/, !1/*string*/)

"往往比;与大括号形成对比的语法如下:

// C# code:
var numbers2 = new Dictionary<Int32, String>
{
    {7, "seven"},
    {9, "nine"},
    {13, "thirteen"}
};
// IL code snippet:
// ----------
// IL_0033: ldstr        "seven"
// IL_0038: callvirt     instance void class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::Add(!0/*int32*/, !1/*string*/)

…正如您所看到的,结果是对Add的调用,正如预期的那样。(人们只能假设上述MSDN上的文本尚未更新。)

到目前为止,我已经发现了一个这种差异实际上很重要的情况,那就是古怪的System.Collections.Specialized.NameValueCollection。这种方法允许一个键指向多个值。初始化可以通过以下两种方式完成:
const String key = "sameKey";
const String value1 = "value1";
const String value2 = "value2";
var collection1 = new NameValueCollection
{
    {key, value1},
    {key, value2}
};
var collection2 = new NameValueCollection
{
    [key] = value1,
    [key] = value2
};

…但是由于只有前者调用NameValueCollection::Add(string, string)的方式不同,所以在查看每个集合的内容时结果不同;

collection1[key] = "value1,value2"

collection2[key] = "value2"

我意识到旧语法和IEnumerable接口之间存在联系,以及编译器如何通过命名约定等方式找到Add方法。我也意识到任何索引器类型服从新语法的好处,正如前面的答案所讨论的那样。

从你的角度来看,也许这些都是你所期望的功能,但我没有想到其中的含义,我很想知道更多。

所以,我想知道MSDN或其他地方是否有文档来源来澄清语法选择带来的行为差异。我还想知道您是否知道在初始化NameValueCollection时,这种选择可能会产生这样的影响的任何其他示例。

集合初始化式语法的区别

我想要得到最终的澄清,你必须去看说明书。c# 6规范还没有"正式"发布,但是有一个非官方的草案。

这里有趣的是,尽管它位于编程指南中,索引器语法不是集合初始化项,而是对象初始化项。源自7.6.11.3 'Collection Initializers':

集合初始化项由一系列元素初始化项组成,由{和}标记括起,并以。分隔逗号。每个元素初始化式指定要添加到被初始化的集合对象中的元素,和由{和}标记包围并以逗号. ...分隔的表达式列表组成将集合初始化项应用于的集合对象必须是实现的类型System.Collections.IEnumerable或发生编译时错误。对于按顺序指定的每个元素集合初始化器调用目标对象上的Add方法,元素初始化器的表达式列表为参数列表

出自7.6.11.2 '对象初始化器':

对象初始化项由一系列成员初始化项组成,由{和}标记括起,并用逗号。每个member_initializer指定初始化的目标。标识符必须命名可访问的要初始化的对象的字段或属性,而方括号中的argument_list必须指定初始化对象上可访问索引器的参数。

以以下为例:

public class ItemWithIndexer
{
    private readonly Dictionary<string, string> _dictionary = 
        new Dictionary<string, string>();
    public string this[string index]
    {
        get { return _dictionary[index]; }
        set { _dictionary[index] = value; }
    }
}

请注意,这个类不满足应用集合初始化器的要求:它没有实现IEnumerableAdd方法,因此任何以这种方式初始化的尝试都会导致编译时错误。然而,这个以索引器为目标的对象初始化器可以编译和工作(参见下面的代码):

var item = new ItemWithIndexer
{
    ["1"] = "value"
};