Подтвердить что ты не робот

Я не понимаю, зачем нам нужно "новое" ключевое слово

Я новичок в С#, на фоне С++. В С++ вы можете сделать это:

class MyClass{
....
};
int main()
{
   MyClass object; // this will create object in memory
   MyClass* object = new MyClass(); // this does same thing
}

В то время как в С#:

class Program
{
    static void Main(string[] args)
    {
        Car x;
        x.i = 2;
        x.j = 3;
        Console.WriteLine(x.i);
        Console.ReadLine();

    }
}
class Car
{
    public int i;
    public int j;


}

вы не можете этого сделать. Интересно, почему Car x не будет выполнять свою работу.

4b9b3361

Ответ 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, чтобы указать, что новое хранилище инициализируется для объекта.

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

Ответ 2

В С# вы можете сделать аналогичную вещь:

  // please notice "struct"
  struct MyStruct {
    ....
  }

  MyStruct sample1; // this will create object on stack
  MyStruct sample2 = new MyStruct(); // this does the same thing

Вспомним, что примитивы типа int, double и bool также имеют тип struct, поэтому, хотя принято писать

  int i;

мы можем также написать

  int i = new int(); 

в отличие от С++, С# не использует указатели (в безопасном режиме) для экземпляров, однако С# имеет объявления class и struct:

  • class: у вас есть ссылка на экземпляр,       память выделяется в куче,        new является обязательным; аналогично MyClass* в С++

  • struct: у вас есть значение,        память (обычно) выделяется в стеке,        new является необязательным; аналогично MyClass в С++

В вашем конкретном случае вы можете просто превратить Car в struct

struct Car
{
    public int i;
    public int j;
}

и поэтому фрагмент

Car x; // since Car is struct, new is optional now 
x.i = 2;
x.j = 3;

будет правильным

Ответ 3

В С# объекты типа class всегда выделяются в куче, т.е. переменные таких типов всегда являются ссылками ( "указатели" ). Просто объявление переменной такого типа не вызывает выделение объекта. Выделение объекта class в стеке, как это обычно бывает в С++, не является (вообще) вариантом в С#.

Локальные переменные любого типа, которые не были назначены, считаются неинициализированными, и они не могут быть прочитаны до тех пор, пока они не будут назначены. Это выбор дизайна (другим способом было бы присвоить default(T) каждой переменной во время объявления), которая кажется хорошей идеей, потому что она должна защитить вас от некоторых ошибок программирования.

Это похоже на то, как в С++ не было смысла говорить SomeClass *object; и никогда ничего не присваивать ему.

Поскольку в С# все переменные типа class являются указателями, выделение пустого объекта при объявлении переменной приведет к неэффективному коду, когда вы действительно хотите назначить значение переменной позже, например, в таких ситуациях:

// Needs to be declared here to be available outside of `try`
Foo f;

try { f = GetFoo(); }
catch (SomeException) { return null; }

f.Bar();

или

Foo f;

if (bar)
    f = GetFoo();
else
    f = GetDifferentFoo();

Ответ 4

игнорирование стека против кучи вещей:

потому что С# сделал плохое решение для копирования С++, когда они должны были просто сделать синтаксис

Car car = Car()

(или что-то подобное). Наличие "нового" является излишним.

Ответ 5

Когда вы используете ссылочные типы, то в этом утверждении

Car c = new Car();

создаются две сущности: ссылка с именем c на объект типа Car в стеке и объект типа Car в куче.

Если вы просто напишете

Car c;

тогда вы создаете неинициализированную ссылку (при условии, что c является локальной переменной), которая указывает на никуда.

На самом деле это эквивалентно коду С++, где вместо ссылок используются указатели.

Например

Car *c = new Car();

или просто

Car *c;

Разница между С++ и С# заключается в том, что С++ может создавать экземпляры классов в стеке, например

Car c;

В С# это означает создание ссылки типа Car, что, как я сказал, указывает нигде.

Ответ 6

В руководстве по программированию Microsoft:

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

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

Структура - это тип значения. Когда создается структура, переменная, которой назначена структура, содержит фактические данные структуры. Когда struct присваивается новой переменной, она копируется. Поэтому новая переменная и исходная переменная содержат две отдельные копии одних и тех же данных. Изменения, внесенные в одну копию, не влияют на другую копию.

Я думаю, что в примере на С# вы пытаетесь присвоить значения нулевому указателю. В переводе С++ это выглядело бы так:

Car* x = null;
x->i = 2;
x->j = 3;

Это, очевидно, будет компилироваться, но сбой.