2016-08-23 33 views
5

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 [鍵] = 「值1,值」

collection2 [鍵] = 「VALUE2」

我知道有舊的語法和IEnumerable接口之間的連接,和編譯器如何通過命名約定等找到Add方法。我也意識到任何索引器類型受益於新語法的好處,如之前的this SO answer中所討論的。

從您的角度來看,也許這些都是預期的功能,但這些影響並沒有發生在我身上,我很想知道更多。

所以,我想知道是否有MSDN或其他地方的文檔來源闡明瞭語法選擇帶來的行爲差異。我還想知道,如果您知道其他任何示例,這些選擇可能會對初始化NameValueCollection產生如此影響。

+4

您參考不說,這上面的「新」語法文檔:*你可以,如果集合支持索引指定索引元素。 *。對我而言,這意味着它將使用索引器來進行設置。 –

+0

@CharlesMager我正要發佈類似看到[這個]後(http://stackoverflow.com/questions/28076127/c6s-new-collection-initializer-澄清) – Rawling

+0

@CharlesMager這是真的你說什麼,但我我們幾乎不會說這是一個「澄清」,也許除了已經開明的一個;)而且,這個問題 - 就行爲差異而言 - 顯然只適用於同時受到「索引」和「增加」的類型,這個頁面上也沒有討論過。 –

回答

4

我想最終澄清,你必須去規範。 C#6規範不是「正式」發佈的,但有一個unofficial draft可用。

這裏有什麼有趣的是,儘管它在編程指南中的位置,索引器語法是而不是集合初始值設定項,它是一個對象初始值設定項。從7.6.11.3 'Collection Initializers'

一個集合初始化由元件的初始化,由{和}記號包圍並通過 逗號分隔的序列組成。 每個元素初始值設定項指定要添加到正在初始化的集合對象的元素,而 由以{和}標記包圍並用逗號分隔的表達式列表組成。 ...應用了集合初始值設定項的集合對象必須是實現了 System.Collections.IEnumerable的類型,否則會發生編譯時錯誤。 對於爲了每個指定的元件,所述 集合初始化調用目標對象上的添加方法與元件初始化的表達列表作爲 參數列表

而且從7.6.11.2 'Object Intializers'

的對象初始值設定項由一系列成員初始值設定項構成,由{和}標記括起來,並以 逗號分隔。每個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; } 
    } 
} 

注意,此類符合要求的應用了集合初始化:它沒有實現IEnumerable或有Add方法,所以任何嘗試以這種方式進行初始化會導致編譯時錯誤。這個對象初始化目標索引將編譯和工作,但(見this fiddle):

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