Кажется, что синтаксис инициализатора объекта C # 3.0 позволяет исключить пару круглых скобок открытия / закрытия в конструкторе, когда существует конструктор без параметров. Пример:
var x = new XTypeName { PropA = value, PropB = value };
В отличие от:
var x = new XTypeName() { PropA = value, PropB = value };
Мне любопытно, почему пара скобок открытия / закрытия конструктора здесь необязательна XTypeName
?
c#
syntax
types
language-design
initializer
Джеймс Данн
источник
источник
Ответы:
Этот вопрос был темой моего блога 20 сентября 2010 года . Ответы Джоша и Чада («они не добавляют ценности, зачем они нужны?» И «для устранения избыточности») в основном верны. Чтобы конкретизировать это немного подробнее:
Возможность исключать список аргументов как часть «более крупной функции» инициализаторов объектов соответствовала нашей планке для «слащавых» функций. Некоторые моменты мы рассмотрели:
Взгляните еще раз на этот список критериев выше. Один из них заключается в том, что это изменение не вносит никакой новой двусмысленности в лексический, грамматический или семантический анализ программы. Предлагаемое вами изменение действительно вносит неоднозначность семантического анализа:
Строка 1 создает новый C, вызывает конструктор по умолчанию, а затем вызывает метод экземпляра M для нового объекта. Строка 2 создает новый экземпляр BM и вызывает его конструктор по умолчанию. Если бы круглые скобки в строке 1 были необязательными, тогда строка 2 была бы неоднозначной. Тогда мы должны были бы придумать правило, разрешающее двусмысленность; мы не могли сделать это ошибкой потому что это было бы критическим изменением, которое превращает существующую легальную программу C # в неработающую программу.
Поэтому правило должно быть очень сложным: по сути, круглые скобки необязательны только в тех случаях, когда они не вносят двусмысленности. Нам нужно будет проанализировать все возможные случаи, когда возникают двусмысленности, а затем написать код в компиляторе для их обнаружения.
В этом свете вернитесь и посмотрите на все упомянутые мной затраты. Сколько из них теперь стали большими? Сложные правила требуют больших затрат на проектирование, спецификацию, разработку, тестирование и документацию. Сложные правила с гораздо большей вероятностью вызовут проблемы с неожиданным взаимодействием с функциями в будущем.
Все для чего? Крошечное преимущество для клиентов, которое не добавляет языку новой репрезентативной силы, но добавляет сумасшедшие угловые случаи, просто ждущие, чтобы крикнуть «Попалась» какой-то бедной ничего не подозревающей душе, которая сталкивается с этим. Подобные функции немедленно удаляются и помещаются в список «никогда не делай этого».
Это было сразу ясно; Я хорошо знаком с правилами C # для определения того, когда ожидается имя, разделенное точками.
Все трое. В основном мы просто смотрим на спецификации и лапшу, как я сделал выше. Например, предположим, что мы хотим добавить в C # новый префиксный оператор под названием «frob»:
(ОБНОВЛЕНИЕ:
frob
конечноawait
; анализ здесь - это, по сути, анализ, который команда разработчиков прошла при добавленииawait
.)«frob» здесь похоже на «новый» или «++» - оно стоит перед каким-либо выражением. Мы определяли желаемый приоритет, ассоциативность и т. Д., А затем начинали задавать такие вопросы, как «что, если в программе уже есть тип, поле, свойство, событие, метод, константа или локальный объект с именем frob?» Это немедленно привело бы к таким случаям, как:
Означает ли это, что «выполнить операцию frob над результатом x = 10 или создать переменную типа frob с именем x и присвоить ей 10?» (Или, если frobbing создает переменную, это может быть присвоение 10. В
frob x
конце концов,*x = 10;
анализирует и допустимо, еслиx
естьint*
.)Означает ли это «заморозить результат унарного оператора плюса над x» или «добавить выражение frob к x»?
И так далее. Чтобы устранить эту двусмысленность, мы можем ввести эвристику. Когда вы говорите «var x = 10;» это неоднозначно; это может означать «вывести тип x» или «x имеет тип var». Итак, у нас есть эвристика: мы сначала пытаемся найти тип с именем var, и только если он не существует, мы делаем вывод о типе x.
Или мы можем изменить синтаксис, чтобы он не был неоднозначным. Когда они разрабатывали C # 2.0, у них была такая проблема:
Означает ли это «yield x в итераторе» или «вызов метода yield с аргументом x»? Изменив его на
это сейчас однозначно.
В случае необязательных скобок в инициализаторе объекта легко понять, есть ли введенная двусмысленность или нет, потому что количество ситуаций, в которых допустимо вводить что-то, что начинается с {, очень мало . По сути, это просто различные контексты операторов, лямбда-выражения операторов, инициализаторы массивов и все такое. Легко рассуждать по всем случаям и показать, что нет двусмысленности. Обеспечить работоспособность IDE несколько сложнее, но это можно сделать без особых проблем.
Такого рода возни со спецификацией обычно бывает достаточно. Если это особенно сложная функция, мы выбираем более тяжелые инструменты. Например, при разработке LINQ один из разработчиков компилятора и один из разработчиков IDE, имеющих опыт работы в теории синтаксического анализатора, создали себе генератор синтаксического анализатора, который мог бы анализировать грамматики на предмет неоднозначности, а затем загружать в него предлагаемые грамматики C # для понимания запросов. ; таким образом было обнаружено много случаев, когда запросы были неоднозначными.
Или, когда мы выполняли расширенный вывод типов на лямбдах в C # 3.0, мы писали наши предложения, а затем отправляли их через пруд в Microsoft Research в Кембридже, где языковая группа была достаточно хороша, чтобы разработать формальное доказательство того, что предложение вывода типов было теоретически здравый.
Конечно.
В C # 1 ясно, что это значит. Это то же самое, что:
То есть он вызывает G с двумя аргументами типа bools. В C # 2 это могло означать то же, что и в C # 1, но это также могло означать «передать 0 универсальному методу F, который принимает параметры типа A и B, а затем передать результат F в G». Мы добавили в синтаксический анализатор сложную эвристику, которая определяет, какой из двух случаев вы, вероятно, имели в виду.
Точно так же приведения неоднозначны даже в C # 1.0:
Это «приведение -x к T» или «вычесть x из T»? Опять же, у нас есть эвристика, которая делает хорошее предположение.
источник
Потому что так был указан язык. Они не добавляют ценности, так зачем их включать?
Это также очень похоже на массивы с неявной типизацией
Ссылка: http://msdn.microsoft.com/en-us/library/ms364047%28VS.80%29.aspx
источник
Это было сделано для упрощения конструкции объектов. Разработчики языка (насколько мне известно) конкретно не сказали, почему они считают, что это было полезно, хотя это явно упоминается на странице спецификации C # версии 3.0 :
Я полагаю, что они посчитали, что скобки в этом случае не нужны, чтобы показать намерение разработчика, поскольку инициализатор объекта вместо этого показывает намерение построить и установить свойства объекта.
источник
В вашем первом примере компилятор делает вывод, что вы вызываете конструктор по умолчанию (спецификация языка C # 3.0 гласит, что если скобки не указаны, вызывается конструктор по умолчанию).
Во втором вы явно вызываете конструктор по умолчанию.
Вы также можете использовать этот синтаксис для установки свойств при явной передаче значений конструктору. Если бы у вас было следующее определение класса:
Все три утверждения действительны:
источник
Я не Эрик Липперт, поэтому я не могу сказать наверняка, но я предполагаю, что это потому, что пустая скобка не нужна компилятору для вывода конструкции инициализации. Таким образом, информация становится избыточной и ненужной.
источник