developer tip

키워드 "new"는 C #의 구조체에 어떤 역할을합니까?

optionbox 2020. 11. 25. 07:53
반응형

키워드 "new"는 C #의 구조체에 어떤 역할을합니까?


C #에서 Structs는 값 측면에서 관리되고 개체는 참조에 있습니다. 내 이해에서 클래스의 인스턴스를 만들 때 키워드 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가 명령문에 무엇을 하는가? . 구조체가 객체가 될 수 없다면 new여기서 인스턴스화 하는 것은 무엇 입니까?


에서 struct (C# Reference)MSDN에서 :

new 연산자를 사용하여 struct 객체를 생성하면 생성되고 적절한 생성자가 호출됩니다. 클래스와 달리 구조체는 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);
        }
    }
}

그것으로 놀아 라. 생성자를 제거하고 어떤 일이 발생하는지 확인하십시오. 하나의 변수 만 초기화하는 생성자를 사용해보십시오 (하나를 주석 처리했습니다 ... 컴파일되지 않습니다). 키워드를 사용하거나 사용하지 않고 시도하십시오 (몇 가지 예를 주석으로 처리하고 주석을 제거하고 시도해 봅니다).


잡아 이 스레드에서 에릭 Lippert의의 훌륭한 대답을. 그를 인용하려면 :

값 유형을 "새로 작성"하면 세 가지가 발생합니다. 첫째, 메모리 관리자는 단기 저장 공간에서 공간을 할당합니다. 둘째, 생성자는 단기 저장 위치에 대한 참조를 전달합니다. 생성자가 실행 된 후에는 단기 저장 위치에 있던 값이 해당 값의 저장 위치에 복사됩니다. 값 유형의 변수는 실제 값을 저장합니다.

(컴파일러가 부분적으로 구성된 구조체를 사용자 코드에 노출하지 않는다고 컴파일러가 결정할 수있는 경우 컴파일러는이 세 단계를 한 단계로 최적화 할 수 있습니다. 즉, 컴파일러는 최종 참조를 단순히 전달하는 코드를 생성 할 수 있습니다. 생성자에 저장 위치를 ​​할당하여 하나의 할당과 하나의 복사본을 저장합니다.)

( 정말 하나이기 때문에이 대답을 )


"new MyStuct ()"를 사용하면 모든 필드가 특정 값으로 설정됩니다. 위의 경우에는 차이가 없습니다. ms.name을 설정하는 대신 읽을 위치를 지정하는 대신 VS에서 "할당되지 않은 필드 '이름'사용 가능"오류가 발생합니다.


객체 나 구조체가 존재할 때마다 모든 필드도 함께 존재합니다. 이러한 필드 중 하나가 구조체 유형이면 모든 중첩 필드도 존재합니다. 배열이 생성되면 모든 요소가 존재합니다 (위와 같이 해당 요소가 구조체 인 경우 해당 구조체의 필드도 존재합니다). 이 모든 것은 생성자 코드가 실행되기 전에 발생합니다.

.net에서 struct 생성자는 사실상 struct를 'out'매개 변수로 취하는 메소드에 지나지 않습니다. C #에서 구조체 생성자를 호출하는 식은 임시 구조체 인스턴스를 할당하고 생성자를 호출 한 다음 해당 임시 인스턴스를 식의 값으로 사용합니다. 이는 생성자에 대해 생성 된 코드가 모든 필드를 제로화하여 시작되지만 호출자의 코드가 생성자가 대상에서 직접 작동하도록 시도하는 vb.net과는 다릅니다. 예 : myStruct = new myStructType(whatever)vb.net myStruct에서는 생성자의 첫 번째 문이 실행되기 전에 지워집니다 . 생성자 내에서 생성중인 객체에 대한 모든 쓰기는에서 즉시 작동합니다 myStruct.


구조체에서 new키워드는 불필요하게 혼동됩니다. 아무것도하지 않습니다. 생성자를 사용하려는 경우에만 필요합니다. 그것은 않습니다 하지 을 수행합니다 new.

의 일반적인 의미는 new영구 저장을 할당하는 것입니다 (힙에.) C ++와 같은 언어는 허용 new myObject()하거나 myObject(). 둘 다 동일한 생성자를 호출합니다. 그러나 전자는 새로운 객체를 생성하고 포인터를 반환합니다. 후자는 단순히 임시를 만듭니다. 모든 구조체 또는 클래스는 둘 중 하나를 사용할 수 있습니다. new선택이며 의미가 있습니다.

C #은 선택권을주지 않습니다. 클래스는 항상 힙에 있고 구조체는 항상 스택에 있습니다. new구조체 에서 실제를 수행하는 것은 불가능합니다 . 숙련 된 C # 프로그래머가 이에 익숙합니다. 그들이 볼 때 그들은 as just 구문 ms = new MyStruct();을 무시하는 것을 알고 new있습니다. 그들은 ms = MyStruct()단순히 기존 객체에 할당 하는 것처럼 작동한다는 것을 알고 있습니다 .

이상하게도 (?), 클래스에는 new. c=myClass();허용되지 않습니다 (생성자를 사용하여 기존 객체의 값을 설정합니다 c.) c.init();. 따라서 선택의 여지가 없습니다. 생성자는 항상 클래스에 할당하고 구조체에는 할당하지 않습니다. new언제나 장식입니다.

new구조체에 fake를 요구하는 이유 는 구조체를 클래스로 쉽게 변경할 수 있기 때문이라고 생각합니다 ( myStruct=new 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 코드에 주석으로 표시됩니다. 그리고 OpCodes.InitobjOpCodes.Newobj를 살펴볼 수 있습니다. 값 유형은 일반적으로 OpCodes.Initobj로 초기화 되지만 MSDN에서 OpCodes.Newobj 도 사용됩니다.

  • OpCodes.Newobj의 설명

    Value types are not usually created using newobj. They are usually allocated either as arguments or local variables, using newarr (for zero-based, one-dimensional arrays), or as fields of objects. Once allocated, they are initialized using Initobj. However, the newobj instruction can be used to create a new instance of a value type on the stack, that can then be passed as an argument, stored in a local, and so on.

For each value type which is numeric, from byte to double, has a defined op-code. Although they are declared as struct, there's some difference in the generated IL as shown.

Here are two more things to mention:

  1. ValueType itself is declared a abstract class

    That is, you cannot new it directly.

  2. structs cannot contain explicit parameterless constructors

    That is, when you new a struct, you would fall into the case above of either NewMyLong or NewMyLongWithValue.

To summarize, new for the value types and structures are for the consistency of the language concept.

참고URL : https://stackoverflow.com/questions/9207488/what-does-the-keyword-new-do-to-a-struct-in-c

반응형