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

Что делает ключевое слово "new" для структуры на С#?

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

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

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

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

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

4b9b3361

Ответ 1

От struct (C# Reference) в MSDN:

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

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

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


Edit

Там довольно длинный поток комментариев, поэтому я подумал, что добавлю немного больше. Я думаю, что лучший способ понять это - дать ему уйти. Создайте консольный проект в 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);
        }
    }
}

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

Ответ 2

Catch Эрик Липперт отличный ответ из этой темы. Чтобы процитировать его:

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

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

(Выполнение этого ответа, так как оно действительно одно)

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

Здесь мы имеем следующее

  • Код

    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 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, имеет определенный op-код. Хотя они объявлены как struct, есть некоторая разница в сгенерированном ИЛ, как показано.

Вот еще две вещи:

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

    То есть вы не можете вводить его напрямую.

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

    То есть, когда вы новый a struct, вы попадете в дело выше либо из NewMyLong, либо NewMyLongWithValue.

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

Ответ 6

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

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

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

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

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