Почему метод Add необходим для инициализации { }?

Чтобы использовать синтаксис инициализации следующим образом:

var contacts = new ContactList
{
    { "Dan", "dan.tao@email.com" },
    { "Eric", "ceo@google.com" }
};

... я понимаю, что мой ContactList тип должен был бы определить Add метод, который занимает два string параметры:

public void Add(string name, string email);

Что меня смущает, так это то, что { } Синтаксис инициализатора кажется наиболее полезным при создании коллекций только для чтения или фиксированного размера. Ведь это должно имитировать синтаксис инициализации для массива, верно? (Хорошо, поэтому массивы не только для чтения; они имеют фиксированный размер.) И, естественно, его можно использовать только тогда, когда известно содержимое коллекции (по крайней мере, количество элементов) во время компиляции.

Таким образом, почти кажется, что основное требование для использования этого синтаксиса инициализатора коллекции (наличие Add Метод и, следовательно, изменяемая коллекция) не соответствует типичному случаю, в котором он был бы наиболее полезным.

Я уверен, что я не задумывался об этом больше, чем команда разработчиков C#; просто кажется, что для этого синтаксиса могли существовать другие правила, которые лучше сочетались бы с его типичными сценариями использования.

Я здесь далеко от базы? Желание использовать { } Синтаксис для инициализации коллекций фиксированного размера не так часто, как я думаю? Какие другие факторы могли повлиять на формулировку требований к этому синтаксису, о которых я просто не думаю?

9 ответов

Решение

Причина этого в том, что он был модернизирован. Я согласен с вами, что использование конструктора, берущего коллекцию, имело бы гораздо больший смысл, но не все существующие классы коллекции реализовали это, и изменение должно (1) работать со всеми существующими коллекциями, (2) не изменять существующие классы в любом путь.

Это компромисс.

Я уверен, что я не задумывался об этом больше, чем команда разработчиков C#; просто кажется, что для этого синтаксиса могли существовать другие правила, которые лучше сочетались бы с его типичными сценариями использования.

Ваш анализ очень хорош; ключевая проблема - последние три слова в приведенном выше утверждении. Каковы реальные типичные сценарии использования?

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

Любой другой сценарий имел более низкий приоритет; функция существует вообще, потому что она помогает заставить LINQ работать.

Команда компиляторов C# 3 была "длинным полюсом" для этого выпуска Visual Studio / .NET - у нас было больше всего дней работы над графиком любой команды, что означало, что каждый день, когда мы откладывали, продукт откладывался. Мы хотели вовремя доставить качественный продукт для всех вас, ребята, а идеал - враг хорошего. Да, эта функция немного неуклюжа и не выполняет абсолютно все, что вы могли бы пожелать, но было важнее получить ее надежную и протестированную для LINQ, чем заставить ее работать для группы неизменяемых типов коллекций, которые в основном не даже существует.

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

Подобные функции есть в списке потенциальных будущих версий гипотетического языка, но не в самом верху списка. Другими словами, давайте сначала разберемся с async / await, прежде чем слишком усердно думать о синтаксических сахарах для неизменной инициализации коллекции.

Это потому, что оператор инициализации является сокращением для CLR. Когда он скомпилирован в байт-код, он вызовет метод Add, который вы определили.

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

Основная причина - синтаксический сахар.

Синтаксис инициализатора только облегчает написание программирования на C#. Это на самом деле не добавляет выразительной силы к языку.

Если инициализатор не требует Add() метод, тогда это было бы намного другой особенностью, чем сейчас. По сути, это не так, как работает C#. Нет буквальной формы для создания общих коллекций.

Строго говоря, это не ответ, но если вы хотите узнать, какие вещи повлияли на дизайн инициализаторов коллекций, вы, вероятно, найдете это интересным:

Насколько я понимаю, синтаксис инициализатора коллекции - это просто синтаксический сахар без особых уловок. Он был разработан частично для поддержки инициализации коллекций внутри запросов Linq:

from a in somewhere
select new {
   Something = a.Something
   Collection = new List<object>() {
      a.Item1,
      a.Item2,
      ...
   }
}

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

Инициализаторы коллекции являются выражениями, поэтому их можно использовать там, где допустимы только выражения, например, инициализатор поля или запрос LINQ. Это делает их существование очень полезным.

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

Что должен использовать синтаксис инициализации, если не Add метод? Синтаксис инициализации "запускается" после запуска конструктора коллекции и полного создания коллекции. Должен быть какой-то способ добавления элементов в коллекцию после ее создания.

Если вы хотите инициализировать коллекцию только для чтения, сделайте это в конструкторе (взяв T[] items аргумент или аналогичный)

Я хотел бы иметь синтаксис инициализатора для неизменяемых типов (как коллекций, так и обычных типов). Я думаю, что это может быть реализовано с помощью специальной перегрузки конструктора с использованием синтаксиса, аналогичного params,

Например что-то вроде этого:

MyClass(initializer KeyValuePair<K,V>[] initialValues)

Но, к сожалению, команда C# еще не реализовала такую ​​вещь:(

Таким образом, мы должны использовать обходной путь, как

MyClass(new KeyValuePair<K,V>[]{...})

теперь

Другие вопросы по тегам