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


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

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 здесь инстанцирования?

6 56

6 ответов:

С struct (C# Reference) на MSDN:

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

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

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


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);
        }
    }
}

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

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

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

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

(что делает этот ответ так как это действительно один)

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

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

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

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. И возможно, вы захотите взглянуть на Коды операций.Initobj и опкоды.Newobj. Тип значения обычно инициализируется с помощью Коды операций.Initobj, но как говорит MSDN опкоды.Newobj также будет использоваться.

  • описание опкоды.Newobj

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

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

вот еще две вещи, чтобы отметить:

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

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

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

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

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

в struct тег 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's в структуры, так что вы можете легко изменить структуру в класс (при условии, что вы всегда использовать myStruct=new myStruct(); когда вы впервые заявляете, что рекомендуемый.)