Ответ 1
Здесь много заблуждений, как в самом вопросе, так и в нескольких ответах.
Позвольте мне начать с изучения предпосылки вопроса. Вопрос: зачем нам ключевое слово new
в С#? " Мотивацией для этого вопроса является этот фрагмент С++:
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
Я критикую этот вопрос по двум причинам.
Во-первых, они не делают то же самое в С++, поэтому вопрос основан на ошибочном понимании языка С++. Очень важно понимать разницу между этими двумя вещами в С++, поэтому, если вы не очень четко понимаете, в чем разница, найдите наставника, который может научить вас, как узнать, в чем разница, и когда использовать их.
Во-вторых, вопрос предполагает - неправильно - что эти два синтаксиса делают одно и то же в С++, а затем, как ни странно, спрашивает: "зачем нам new
в С#?" Разумеется, правильный вопрос, заданный при этом - опять же, ложная предпосылка - "зачем нам new
в С++?" Если эти два синтаксиса выполняют одно и то же - а это не так, то почему в первую очередь есть два синтаксиса?
Таким образом, вопрос основан на ложной предпосылке, и вопрос о С# на самом деле не вытекает из - неправильно понятого - дизайна С++.
Это беспорядок. Выбросьте этот вопрос и задайте более интересные вопросы. И пусть задайте вопрос о С# qua С#, а не в контексте дизайнерских решений С++.
Что делает оператор
new X
в С#, где X - тип класса или структуры? (Пусть игнорируют делегатов и массивы для целей этой дискуссии.)
Новый оператор:
- Вызывает новый экземпляр данного типа; новые экземпляры имеют все поля, инициализированные значениями по умолчанию.
- Запускает конструктор данного типа.
- Производит ссылку на выделенный объект, если объект является ссылочным типом или самим значением, если объект является типом значения.
Хорошо, я уже слышу возражения от программистов на С# там, поэтому пусть их отклоняют.
Возражение: не выделяется новое хранилище, если тип является типом значения, я слышал, вы говорите. Ну, спецификация С# не согласна с вами. Когда вы скажете
S s = new S(123);
для некоторого типа структуры S
, спецификация говорит, что новое временное хранилище распределяется в кратковременном пуле, инициализированном его значениями по умолчанию, конструктор работает с this
, установленным для обращения к хранилищу temp, а затем результирующий объект копируется в S
. Однако компилятору разрешено использовать оптимизацию при копировании, при условии, что он может доказать, что оптимизация не может наблюдаться в безопасной программе. (Упражнение: выясните, при каких обстоятельствах не может быть выполнено исключение копии, укажите пример программы, которая имела бы другое поведение, если бы это было или не использовалось.)
Возражение: действительный экземпляр типа значения может быть создан с использованием default(S)
; никакой конструктор не вызван, слышу вы говорите. Это правильно. Я не сказал, что new
- единственный способ создать экземпляр типа значения.
Фактически, для типа значения new S()
и default(S)
- одно и то же.
Возражение: Является ли конструктор действительно выполненным для ситуаций типа new S()
, если не присутствует в исходном коде на С# 6, я слышу, что вы говорите. Это "если дерево падает в лес, и никто его не слышит, это звучит?" вопрос. Есть ли разница между вызовом конструктора, который ничего не делает, и вообще не звонит? Это не интересный вопрос. Компилятор может свободно выполнять вызовы, которые он знает, ничего не делает.
Предположим, что у нас есть переменная типа значения. Нужно ли инициализировать переменную экземпляром, созданным
new
?
Нет. Переменные, которые автоматически инициализируются, такие как поля и элементы массива, будут инициализированы значением по умолчанию, то есть значением структуры, где все поля сами по себе являются значениями по умолчанию.
Формальные параметры будут, очевидно, инициализированы аргументом.
Локальные переменные типа значения должны быть обязательно назначены с чем-то перед чтением полей, но это не должно быть выражение new
.
Таким образом, переменные типа значения автоматически инициализируются с эквивалентом
default(S)
, если они не являются локальными?
Да.
Почему бы не сделать то же самое для локальных жителей?
Использование неинициализированного локального файла сильно связано с неправильным кодом. Язык С# запрещает это, потому что при этом обнаруживаются ошибки.
Предположим, что у нас есть переменная ссылочного типа. Мы должны инициализировать
S
экземпляром, созданнымnew
?
Нет. Автоматически инициализирующие переменные будут инициализированы нулевым значением. Местные жители могут быть инициализированы любой ссылкой, включая null
, и должны быть обязательно назначены перед чтением.
Таким образом, переменные ссылочного типа автоматически инициализируются с помощью
null
, если они не являются локальными?
Да.
Почему бы не сделать то же самое для локальных жителей?
По той же причине. Вероятная ошибка.
Почему бы не автоматически инициализировать переменные ссылочного типа, вызвав конструктор по умолчанию автоматически? То есть, почему бы не сделать
R r;
таким же, какR r = new R();
?
Ну, во-первых, у многих типов нет конструктора по умолчанию или, если угодно, любого доступного конструктора вообще. Во-вторых, кажется странным иметь одно правило для неинициализированного локального или поля, другое правило для формального и еще одно правило для элемента массива. В-третьих, существующее правило очень просто: переменная должна быть инициализирована значением; это значение может быть любым, что вам нравится; Почему предположение, что требуется новый экземпляр, требуется? Было бы странно, если бы этот
R r;
if (x) r = M(); else r = N();
вызвало запуск конструктора для инициализации r
.
Оставив в стороне семантику оператора
new
, почему синтаксически необходимо иметь такой оператор?
Это не так. Существует несколько альтернативных синтаксисов, которые могут быть грамматическими. Наиболее очевидным было бы просто полностью исключить new
. Если у нас есть класс C
с конструктором C(int)
, мы можем просто сказать C(123)
вместо new C(123)
. Или мы могли бы использовать синтаксис типа C.construct(123)
или некоторые такие вещи. Существует несколько способов сделать это без оператора new
.
Так зачем?
Во-первых, С# был разработан для непосредственного знакомства с пользователями С++, Java, JavaScript и других языков, которые используют new
, чтобы указать, что новое хранилище инициализируется для объекта.
Во-вторых, очень желателен правильный уровень синтаксической избыточности. Создание объекта является особенным; мы хотим вызывать, когда это происходит со своим собственным оператором.