Что ключевое слово "new" делает со структурой в C #? - PullRequest
60 голосов
/ 09 февраля 2012

В C # структуры управляются в терминах значений, а объекты являются ссылочными. Насколько я понимаю, при создании экземпляра класса ключевое слово new заставляет C # использовать информацию о классе для создания экземпляра, как показано ниже:

class MyClass
{
    ...
}
MyClass mc = new MyClass();

Для структуры вы не создаете объект, а просто устанавливаете переменную в значение:

struct MyStruct
{
    public string name;
}
MyStruct ms;
//MyStruct ms = new MyStruct();     
ms.name = "donkey";

Что я не понимаю, если объявить переменные MyStruct ms = new MyStruct(), что ключевое слово new здесь делает с оператором? , Если struct не может быть объектом, то что является экземпляром new здесь?

Ответы [ 6 ]

56 голосов
/ 09 февраля 2012

С struct (C# Reference) в MSDN:

Когда вы создаете объект структуры с помощью оператора new, он создается и вызывается соответствующий конструктор. В отличие от классов, структуры могут быть созданы без использования оператора new. Если вы не используете new, поля останутся неназначенными, и объект нельзя будет использовать, пока все поля не будут инициализированы.

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

Надеюсь, это прояснит. Если вам нужно разъяснение, дайте мне знать.


Редактировать

Там довольно длинная ветка комментариев, поэтому я решил добавить сюда немного больше. Я думаю, что лучший способ понять это - попробовать. Создайте консольный проект в Visual Studio под названием «StructTest» и скопируйте в него следующий код.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace struct_test
{
    class Program
    {
        public struct Point
        {
            public int x, y;

            public Point(int x)
            {
                this.x = x;
                this.y = 5;
            }

            public Point(int x, int y)
            {
                this.x = x;
                this.y = y;
            }

            // It will break with this constructor. If uncommenting this one
            // comment out the other one with only one integer, otherwise it
            // will fail because you are overloading with duplicate parameter
            // types, rather than what I'm trying to demonstrate.
            /*public Point(int y)
            {
                this.y = y;
            }*/
        }

        static void Main(string[] args)
        {
            // Declare an object:
            Point myPoint;
            //Point myPoint = new Point(10, 20);
            //Point myPoint = new Point(15);
            //Point myPoint = new Point();


            // Initialize:
            // Try not using any constructor but comment out one of these
            // and see what happens. (It should fail when you compile it)
            myPoint.x = 10;
            myPoint.y = 20;

            // Display results:
            Console.WriteLine("My Point:");
            Console.WriteLine("x = {0}, y = {1}", myPoint.x, myPoint.y);

            Console.ReadKey(true);
        }
    }
}

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

16 голосов
/ 09 июня 2013

Catch Отличный ответ Эрика Липперта из этой цепочки. Процитирую его:

Когда вы "новый" тип значения, происходят три вещи.Во-первых, диспетчер памяти выделяет пространство из кратковременного хранилища.Во-вторых, конструктору передается ссылка на место кратковременного хранения.После запуска конструктора значение, которое находилось в кратковременном хранилище, копируется в хранилище для значения, где бы оно ни находилось.Помните, переменные типа значения хранят фактическое значение.

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

( Создание этого ответа, поскольку на самом деле это один )

3 голосов
/ 10 февраля 2012

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

В .net конструктор структуры фактически является не чем иным, как методом, который принимает структуру в качестве параметра out. В C # выражение, которое вызывает конструктор структуры, выделит временный экземпляр структуры, вызовет конструктор для этого и затем использует этот временный экземпляр в качестве значения выражения. Обратите внимание, что это отличается от vb.net, где сгенерированный код для конструктора будет начинаться с обнуления всех полей, но где код от вызывающей стороны будет пытаться заставить конструктор работать непосредственно с назначением. Например: myStruct = new myStructType(whatever) в vb.net очистит myStruct перед выполнением первого оператора конструктора; в конструкторе любая запись в строящийся объект будет немедленно работать на myStruct.

3 голосов
/ 09 февраля 2012

Использование «new MyStuct ()» гарантирует, что для всех полей установлено какое-либо значение. В случае выше, ничто не отличается. Если вместо установки ms.name вы пытаетесь его прочитать, вы получите ошибку «Использование возможного неназначенного поля« имя »» в VS.

0 голосов
/ 29 декабря 2015

В структуре ключевое слово new излишне сбивает с толку.Это ничего не делает.Это просто необходимо, если вы хотите использовать конструктор.Он не выполняет new.

Обычное значение new состоит в том, чтобы выделять постоянную память (в куче). Язык, подобный C ++, позволяет new myObject() или просто myObject().Оба вызывают один и тот же конструктор.Но первый создает новый объект и возвращает указатель.Последний просто создает временнуюЛюбая структура или класс может использовать либо.new - это выбор, и он что-то значит.

C # не дает вам выбора.Классы всегда в куче, а структуры всегда в стеке.Невозможно выполнить настоящий new над структурой.К этому привыкли опытные программисты на C #.Когда они видят ms = new MyStruct();, они знают, что игнорируют new как синтаксис.Они знают, что он действует как ms = MyStruct(), который просто присваивает существующий объект.

Как ни странно (?), Классы требуют new.c=myClass(); не разрешено (использование конструктора для установки значений существующего объекта c.) Вам нужно сделать что-то вроде c.init();.Таким образом, у вас действительно никогда нет выбора - конструкторы всегда выделяют для классов, а не для структур.new всегда является просто украшением.

Я предполагаю, что причина, по которой вам нужны поддельные new в структурах, заключается в том, что вы можете легко изменить структуру в класс (при условии, что вы всегда используете myStruct=new myStruct();, когда высначала объявите, что рекомендуется.)

0 голосов
/ 24 марта 2013

ValueType и структуры являются чем-то особенным в C #.Здесь я покажу вам, что происходит, когда вы новый что-то.

Здесь у нас есть следующие

  • Код

    partial class TestClass {
        public static void NewLong() {
            var i=new long();
        }
    
        public static void NewMyLong() {
            var i=new MyLong();
        }
    
        public static void NewMyLongWithValue() {
            var i=new MyLong(1234);
        }
    
        public static void NewThatLong() {
            var i=new ThatLong();
        }
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public partial struct MyLong {
        const int bits=8*sizeof(int);
    
        public static implicit operator int(MyLong x) {
            return (int)x.m_Low;
        }
    
        public static implicit operator long(MyLong x) {
            long y=x.m_Hi;
            return (y<<bits)|x.m_Low;
        }
    
        public static implicit operator MyLong(long x) {
            var y=default(MyLong);
            y.m_Low=(uint)x;
            y.m_Hi=(int)(x>>bits);
            return y;
        }
    
        public MyLong(long x) {
            this=x;
        }
    
        uint m_Low;
        int m_Hi;
    }
    
    public partial class ThatLong {
        const int bits=8*sizeof(int);
    
        public static implicit operator int(ThatLong x) {
            return (int)x.m_Low;
        }
    
        public static implicit operator long(ThatLong x) {
            long y=x.m_Hi;
            return (y<<bits)|x.m_Low;
        }
    
        public static implicit operator ThatLong(long x) {
            return new ThatLong(x);
        }
    
        public ThatLong(long x) {
            this.m_Low=(uint)x;
            this.m_Hi=(int)(x>>bits);
        }
    
        public ThatLong() {
            int i=0;
            var b=i is ValueType;
        }
    
        uint m_Low;
        int m_Hi;
    }
    

И сгенерированный IL из методов тестового классабудет

  • IL

    // NewLong
    .method public hidebysig static 
        void NewLong () cil managed 
    {
        .maxstack 1
        .locals init (
            [0] int64 i
        )
    
        IL_0000: nop
        IL_0001: ldc.i4.0 // push 0 as int
        IL_0002: conv.i8  // convert the pushed value to long
        IL_0003: stloc.0  // pop it to the first local variable, that is, i
        IL_0004: ret
    } 
    
    // NewMyLong
    .method public hidebysig static 
        void NewMyLong () cil managed 
    {
        .maxstack 1
        .locals init (
            [0] valuetype MyLong i
        )
    
        IL_0000: nop
        IL_0001: ldloca.s i     // push address of i
        IL_0003: initobj MyLong // pop address of i and initialze as MyLong
        IL_0009: ret
    } 
    
    // NewMyLongWithValue 
    .method public hidebysig static 
        void NewMyLongWithValue () cil managed 
    {
        .maxstack 2
        .locals init (
            [0] valuetype MyLong i
        )
    
        IL_0000: nop
        IL_0001: ldloca.s i  // push address of i
        IL_0003: ldc.i4 1234 // push 1234 as int
        IL_0008: conv.i8     // convert the pushed value to long
    
        // call the constructor
        IL_0009: call instance void MyLong::.ctor(int64) 
    
        IL_000e: nop
        IL_000f: ret
    } 
    
    // NewThatLong
    .method public hidebysig static 
        void NewThatLong () cil managed 
    {
        // Method begins at RVA 0x33c8
        // Code size 8 (0x8)
        .maxstack 1
        .locals init (
            [0] class ThatLong i
        )
    
        IL_0000: nop
    
        // new by calling the constructor and push it's reference
        IL_0001: newobj instance void ThatLong::.ctor() 
    
        // pop it to the first local variable, that is, i
        IL_0006: stloc.0
    
        IL_0007: ret
    } 
    

Поведение методов прокомментировано в коде IL.И вы можете посмотреть OpCodes.Initobj и OpCodes.Newobj .Тип значения обычно инициализируется с помощью OpCodes.Initobj , но, как говорит MSDN, также будет использоваться OpCodes.Newobj .

  • описание в OpCodes.Newobj

    Типы значений: , обычно не , созданные с использованием newobj.Они обычно размещаются либо как аргументы, либо как локальные переменные, используя newarr (для основанных на нуле, одномерных массивов), или как поля объектов.После выделения они инициализируются с помощью Initobj. Однако , инструкция newobj может использоваться для создания нового экземпляра типа значения в стеке, который затем может быть передан в качестве аргумента, сохранен в локальном и т. Д.

Для каждого типа значения, который является числовым, от byte до double, имеется определенный код операции.Хотя они объявлены как struct, в сгенерированном IL есть некоторое различие, как показано.

Вот еще две вещи, которые следует упомянуть:

  1. ValueType сам объявлен абстрактный класс

    То естьВы не можете новый это напрямую.

  2. struct s не может содержать явных конструкторов без параметров

    То есть, когда вы new a struct, вы попадете в случайвыше или NewMyLong или NewMyLongWithValue.

Для суммирования , new для типов значений и структур - для согласованности концепции языка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...