2016-08-23 5 views
5

синтаксиса MSDN утверждает, что:разница между сбором Initializer

С помощью коллекции инициализатору вы не должны указывать несколько вызовов метода 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, значение2"

collection2 [ключ] = "значение2"

Я понимаю, что есть связь между старым синтаксисом и интерфейсом IEnumerable и как компилятор находит метод Add по именованию и т. д. Я также понимаю преимущества любого типа индексатора, подверженного новому синтаксису, как обсуждалось ранее в this SO answer.

Возможно, это все ожидаемые функции, с вашей точки зрения, но последствия мне не пригодились, и мне любопытно узнать больше.

Итак, интересно, есть ли источник документации в MSDN или в другом месте, который разъясняет эту разницу в поведении, которая поставляется с выбором синтаксиса. Я также задаюсь вопросом, знаете ли вы о других примерах, где этот выбор может иметь такое влияние, как при инициализации NameValueCollection.

+4

Документов вы ссылаетесь, чтобы сделать сказать, что это выше «новой» синтаксиса: * Можно указать индексированные элементы, если коллекция поддерживает индексацию. *. Для меня это означает, что он будет использовать индексатор для настройки. –

+0

@CharlesMager Я как раз собирался опубликовать что-то подобное, увидев [это] (http://stackoverflow.com/questions/28076127/c6s-new-collection-initializer-clarification) – Rawling

+0

@CharlesMager Это правда, что вы говорите, но я «вряд ли назову это« разъяснением », за исключением, возможно, для уже просвещенного;) Кроме того, вопрос - насколько различия в поведении идут - очевидно, применимы только к типам, которые подвержены« индексированию »и« добавлению », то, что также не обсуждается на этой странице. –

ответ

4

Я полагаю, что для окончательного разъяснения вам нужно перейти к спецификации. Спецификация C# 6 не «официально», но есть unofficial draft.

Что интересно здесь, что, несмотря на его расположение в Руководстве по программированию, синтаксис индексатора не инициализатор коллекции, это инициализатор объекта.От 7.6.11.3 'Collection Initializers':

Коллекция инициализатор состоит из последовательности элементов инициализаторах, вложенных {и} лексем, разделенных запятыми . Каждый инициализатор элемента указывает элемент, который должен быть добавлен к инициализированному объекту коллекции, а состоит из списка выражений, заключенных {и} токенами и разделенных запятыми. ... Объект коллекции, к которому применяется инициализатор коллекции, должен иметь тип, который реализует System.Collections.IEnumerable или ошибка времени компиляции. Для каждого заданного элемента в порядке, коллекция инициализатор вызывает метод Add на целевом объекте со списком экспрессии элемента инициализаторе в списке аргумента

И от 7.6.11.2 'Object Intializers':

An Инициализатор объекта состоит из последовательности инициализаторов элементов, заключенных {и} токенами и разделенных запятыми . Каждый member_initializer обозначает цель для инициализации. Идентификатор должен указать доступное поле или свойство инициализированного объекта, тогда как аргумент-лист, заключенный в квадратные скобки, должен указывать аргументы для доступного индексатора на инициализированном объекте.

Возьмите это в качестве примера:

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" 
}; 
Смежные вопросы