2016-06-29 3 views
31

Я новичок в C#, на фоне C++. В C++ вы можете сделать это:Я не понимаю, зачем нам нужно «новое» ключевое слово

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

Принимая во внимание, в C#:

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 не будет выполнять свою работу.

+3

Вот как C# есть. Для создания нового экземпляра класса вам понадобится 'new', поэтому это будет' Car x = new Car(); ' – Pikoh

+7

Я часто говорил, что проще перейти от C++ к C# или Java, чем к другой способ вокруг. Имея четкое понимание основ C++, сбор C# или Java довольно прост. Но оба они делают так много вещей под обложками и которые, как правило, не очень хорошо объясняются в традиционных ресурсах, которые учат C# и Java, что при переходе от любого из этих двух к C++ это всегда очень и очень разочаровывает. –

+74

Btw, 'Myclass object;' делает что-то отличное от 'Myclass * object = new MyClass();' – MaciekGrynda

ответ

15

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

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

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

Поскольку в C# всех 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(); 
+3

Немного небрежно сказать, что «типы классов - это ссылки». Типы классов - это типы классов. Верно то, что переменная *, объявленная с типом класса *, представляет собой нулевую ссылку на объект. –

+0

@KerrekSB: Лучше? Я признаю, что говорить о куче, вероятно, рассматривается как деталь реализации, но так оно и работает на практике. –

+0

Да, очень приятно, теперь это очень ясно. Благодаря! –

28

В C# вы можете сделать похожи вещь:

// 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(); 

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

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

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

В вашем конкретном случае, вы можете просто включить 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; 

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

+34

Я бы возражал против обычного заблуждения, которое постоянно бросается вокруг и создает удивительно обобщенное ложное убеждение: классы идут в кучу, структуры идут в стек. Это абсолютно неверно. Решение того, что происходит в стеке и что происходит в куче, зависит от ожидаемого срока жизни объекта, а не от его характера; структура, которая не может быть проверена как непродолжительная, перейдет в кучу: 'class myClass {int i = 1; // это будет в куче, а не в стеке. } '. – InBetween

+5

@InBetween: Спасибо! Я вижу https://blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-truth-about-value-types/ –

+0

Можете ли вы объяснить мне, в чем разница между хранением в куче и стеке? Насколько я знаю, это http://www.cplusplus.com/reference/stack/stack/. Я не знаю, что это такое с точки зрения памяти. – user6528398

6

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

Car c = new Car(); 

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

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

Car c; 

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

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

Например

Car *c = new Car(); 

или просто

Car *c; 

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

Car c; 

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

11

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

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

Car car = Car() 

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

+3

+1 для единственного ответа до сих пор, чтобы на самом деле это исправить. Нет фундаментальной необходимости в «новом» keywor d в C#, как есть в C++, и есть другие языки CLR, фактически, которые не используют его (или любой эквивалент, специфичный для языка) вообще. –

+4

Кроме того, конечно, что теперь вводит конфликты, если у вас есть метод под названием 'Car()' в области, где вы пытаетесь вызвать конструктор. В «новой» части дается понять, что вы пытаетесь вызвать конструктор. Я могу увидеть аргумент для 'Car.new()' вместо этого, но просто «Car()» кажется мне плохой идеей. –

+0

Да, добавление 'new' допускает двусмысленный синтаксис, между 1) contructors/методами/ссылкой на класс и 2) создание объекта. Поэтому, хотя это не обязательно «необходимо», это долгий путь к тому, чтобы отличить то, что вы используете. – vapcguy

38

Здесь есть много неправильных представлений, как в самом вопросе, так и в нескольких ответах.

Позвольте мне начать рассмотрение предпосылки вопроса. Вопрос в том, «зачем нам нужно ключевое слово new в C#?» Мотивация вопроса заключается в этом фрагменте C++:

MyClass object; // this will create object in memory 
MyClass* object = new MyClass(); // this does same thing 

Я критикую этот вопрос на двух основаниях.

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

Во-вторых, вопрос предполагает - неверно, что эти два синтаксиса выполняют одно и то же в C++, а затем, как ни странно, спрашивает: «Почему нам нужно new в C#?» Разумеется, правильный вопрос, который следует задать, - это опять-таки ложная предпосылка: «зачем нам new в C++?» Если эти два синтаксиса делают то же самое, чего нет, то почему у вас есть два синтаксиса?

Таким образом, вопрос основывается на ложной предпосылке, и вопрос о C# на самом деле не вытекает из - непонятого - дизайна C++.

Это беспорядок. Давайте вышлем этот вопрос и зададим несколько более сложных вопросов. И давайте зададим вопрос о C# qua C#, а не в контексте проектных решений C++.

Что делает оператор new X в C#, где X - класс или тип структуры? (Давайте игнорировать делегатов и массивы для целей этой дискуссии.)

Новый оператор:

  • Вызывает новый экземпляр данного типа, которые будут выделены; новые экземпляры имеют все поля, инициализированные значениями по умолчанию.
  • Вызывает конструктор данного типа для выполнения.
  • Производит ссылку на выделенный объект, если объект является ссылочным, или само значение, если объект является типом значения.

Хорошо, я уже слышу возражения от программистов на C# там, поэтому давайте их отстраняем.

Возражение: нет нового хранилища, если тип является типом значения, я слышу, что вы говорите. Ну, спецификация C# не согласна с вами. Когда вы говорите

S s = new S(123); 

для некоторой структуры типа S, спецификация говорит, что новое временное хранение выделяется на краткосрочный пул, инициализируются значениями по умолчанию, конструктор работает с this набором, чтобы обратиться к и затем результирующий объект скопировал в s. Однако компилятор разрешил использовать оптимизацию при копировании, при условии, что он может доказать, что оптимизация не может наблюдаться в безопасной программе. (Упражнение: выясните, при каких обстоятельствах не может быть выполнено копирование, дайте пример программы, которая имела бы разные типы поведения, если бы элиция была или не использовалась.)

Возражение: действительным экземпляром типа значения может быть с использованием default(S); никакой конструктор не вызван, слышу вы говорите. Правильно. Я не сказал, что new является только способ создания экземпляра типа значения.

Фактически, для значения типа new S() и default(S) - это то же самое.

Возражение: Является ли конструктор действительно выполненным для ситуаций, таких как new S(), если не присутствует в исходном коде на C# 6, я слышу, что вы говорите. Это «если дерево падает в лес, и никто его не слышит, это звучит?» вопрос. Есть ли разница между вызовом конструктора, который ничего не делает, и вообще не звонит? Это не интересный вопрос. Компилятор может свободно выполнять вызовы, которые он знает, ничего не делает.

Предположим, что у нас есть переменная типа значения. Нужно ли инициализировать переменную экземпляром, созданным new?

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

Формальные параметры будут, очевидно, инициализированы аргументом.

Локальные переменные типа значения должны быть обязательно назначены чем-то перед чтением полей, но это не должно быть выражение new.

Так эффективно переменные типа значения автоматически инициализируются эквивалентом default(S), если только они не являются местными жителями?

Да.

Почему бы не сделать то же самое для местных жителей?

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

Предположим, что у нас есть переменная ссылочного типа. Должен ли мы инициализировать 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.

Так почему?

Во-первых, C# был разработан, чтобы быть немедленно знакомы пользователям C++, Java, JavaScript и другие языки, которые используют new для указания нового хранилища для инициализации объекта.

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

+1

«Создание объекта является особенным». Я не знаю, создание объекта в настоящее время не кажется таким особенным. Тем не менее, большой ответ. – Mephy

+2

«В C# 6 вы можете определить конструктор по умолчанию, который вызывается при вызове' new S() ', но не при использовании' default (S) '. - В конце концов эта функция была отброшена, потому что из-за того, что я помню, это вызывало слишком много головных болей. (Вы все равно можете это сделать в IL, и ситуации, в которых он вызывается, не всегда очевидны ...) –

+2

Еще одна вещь, которая может быть полезной, - это то, что из-за соглашений об именах C#, которые они стоят прямо сейчас, if у нас не было «нового», было бы нелегко сказать разницу между использованием конструктора и вызовом метода, который находится в той же области, что и вызывающий. Наличие 'new' помогает устранить эту проблему, вместо того, чтобы требовать полной квалификации вызова метода. –

2

С майкрософт руководство по программированию:

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

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

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

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

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

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

Смежные вопросы