Неоднозначность необязательных скобок. Часть 1

Еще один интересный вопрос со StackOverflow, который я представлю здесь, как обычно, в виде диалога:

В C# 3.0 существует два формата синтаксиса инициализатора объектов (object initializer) (и инициализатора коллекций (collection initialize)): new Foo { Bar = 123 } и new Foo() {Bar = 123 }. Если используется конструктор по умолчанию, список аргументов не является обязательным. Почему комитет по разработке языка принял решение сделать список аргументов необязательным?

Обсуждение этой возможности проходило в 2004-м году, до того, как я присоединился к команде C#, так что любые рассуждение на эту тему будут гипотетическими. Читая архив проектных записей я не нашел конкретного места, где это решение было бы описано со всеми за и против, но я все же нашел там несколько интересных вещей.

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

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

И, в-третьих, казалось вполне естественным опустить аргументы при инициализации объекта или коллекции, поскольку именно так делается с массивами: new int[] {1, 2, 3} и List<int> {1, 2, 3} явно аналогичны.

Хорошо. Тогда более общий вопрос: как вы решили, когда можно добавить этот небольшой синтаксический сахар?

Первый вопрос должен быть таким: «есть ли разумная причина, почему список аргументов должен быть обязательным?» Очевидно, что мы не можем сделать его необязательным, если он является обязательным. Однако в данном случае нет убедительной причины, почему он должен быть обязательным. Пустой список аргументов является избыточным и не несет никакой информации, так что было бы неплохо, если бы его можно было опустить. Кроме того, эта возможность бьет прямо в точку, ведь наиболее вероятным сценарием использования инициализатора объекта или коллекций как раз и является использование его с изменяемым (mutable) объектом, который содержит свойства, не устанавливаемые ни в каком конструкторе, и такие объекты зачастую содержат конструктор по умолчанию. Кажется разумным, что в большинстве случаев список аргументов будет пустым, так что, опять-таки, эта возможность будет полезной.

Однако, «полезность» будет играть какую-то роль только при невысоких затратах на реализацию.

Какие затраты вы рассматриваете?

Мы рассматриваем следующее:

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

Затраты на реализацию в компиляторе: нам в любом случае придется полностью переписать логику анализа «выражения создания объекта» (object creation expression); необязательный список аргументов является лишь небольшой частью реализации более общей функции.

Затраты на разработку IDE: добавляет ли предложенная особенность сложности в анализ неполной программы во время ее набора? IDE должна делать это; а функции, которые добавляют головной боли разработчикам IDE являются плохими, потому что IDE должна действительно классно работать; ей придется выполнять семантический анализ между нажатиями клавиш. Этот «сахар» не должен добавить сложный лексический, грамматический или семантический анализ или привести к его неоднозначности. Это должно хорошо работать с рефакторингом и другими сложными анализами, которые выполняет IDE.

Затраты на тестирование: добавляет ли эта особенность большое количество сценариев, которые сложно тестировать? В данном случае, нет. Проверить такой необязательный синтаксис очень просто. И опять-таки, затраты на тестирование этой особенности являются лишь малой толикой затрат на тестирование всех возможностей.

Затраты на обучение пользователей: добавляет ли эта возможность такой синтаксис, который потребует большого количества документации? Собираемся ли мы записывать видео или писать сообщения в блогах на эту тему? Будем ли мы получать звонки от наших пользователей, которым это непонятно? Нет, нет и еще раз нет.

Затраты на сопровождение: какие могут быть ошибки? Можем ли мы предполагать, что пропустили какое-то безумное взаимодействие с другой функциональностью, которое будет приводить к трудноуловимому и сложноустранимому багу? Или вероятно, что мы справимся нормально с первого раза? Я что-то не припомню, чтобы у нас были какие-то отчеты об ошибках, связанные с этой особенностью. Ее легко реализовать правильно.

Затраты на потерянные возможности: реализуя эту особенность, потратили ли мы много времени и усилий, которые могли бы потрать более разумно для наших пользователей? Не совсем; опять-таки, затраты на реализацию этой особенности были лишь малой толикой того, что было потрачено на реализацию функциональности инициализаторов коллекций и объектов.

Будущие затраты: помешает ли эта особенность реализации какой-то другой функциональности в будущем, или сделает ее более сложной или дорогой? Нет. Кажется маловероятным, что в будущем мы скажем: «мы бы смогли добавить метапрограммирование в C# только если бы не сделали список аргументов необязательным».

Все затраты являются невысокими, а особенность является полезной, вот мы ее и реализовали.

Так почему вы не добавили еще подобных особенностей?

Об этом в следующий раз!

Оригинал статьи