>백엔드 개발 >C#.Net 튜토리얼 >C# 기본 지식 정리 기본 지식(18) 값 유형의 박싱 및 언박싱(1)

C# 기본 지식 정리 기본 지식(18) 값 유형의 박싱 및 언박싱(1)

黄舟
黄舟원래의
2017-02-11 13:49:431362검색

복싱과 언박싱에 대해 자세히 알아보는 것은 사실 매우 흥미롭습니다. 먼저 복싱과 언박싱이 발생하는 이유를 살펴보겠습니다.
다음 코드를 살펴보세요.

    class Program
    {
        static void Main(string[] args)
        {
            ArrayList array = new ArrayList();

            Point p;//分配一个

            for (int i = 0; i < 5; i++)
            {
                p.x = i;//初始化值

                p.y = i;

                array.Add(p);//装箱
            }
        }
    }

    public struct Point
    {
        public Int32 x;

        public Int32 y;
    }

5번 반복하여 매번 Point 값 유형 필드를 초기화한 다음 이를 ArrayList에 넣습니다. Struct는 값형 구조인데 ArrayList에는 무엇이 저장되나요? ArrayList의 Add 메서드를 다시 살펴보겠습니다. MSDN에서 Add 메소드를 볼 수 있습니다:
public virtual int Add(Object value),
Add의 매개변수가 Object 유형임을 알 수 있습니다. 즉, 필요한 매개변수는 객체에 대한 참조입니다. . 즉, 여기의 매개변수는 참조 유형이어야 합니다. 참조 유형이 무엇인지에 대해서는 자세히 설명할 필요가 없습니다. 이는 힙에 있는 객체에 대한 참조일 뿐입니다. 하지만 이해의 편의를 위해 다시 힙과 스택에 대해 이야기해 보겠습니다.
1. 스택 영역(Stack) - 컴파일러에 의해 자동으로 할당 및 해제되며, 함수 매개변수 값, 지역 변수 값 등이 저장됩니다.
2. 힙 영역(heap) - 프로그래머가 할당하고 해제하지 않으면 프로그램 종료 시 OS에 의해 재활용될 수 있습니다.
예:

    class Program
    {
        static void Main(string[] args)
        {
            Int32 n;//这是值类型,存放在栈中,Int32初始值为0
            
            A a;//此时在栈中开辟了空间

            a = new A();//真正实例化后的一个对象则保存在堆中。
        }
    }

    public class A
    {
        public A() { }
    }

위의 질문으로 돌아가서 Add 메소드에는 참조 유형의 매개변수가 필요합니다. 어떻게 해야 합니까? 그런 다음 권투를 사용해야 합니다. 소위 권투는 값 유형을 참조 유형으로 변환하는 것입니다. 변환 과정은 다음과 같습니다.
1. 관리되는 힙에 메모리를 할당합니다. 할당된 메모리 양은 값 형식의 개별 필드에 필요한 메모리 양과 관리되는 힙의 모든 개체에 있는 두 개의 추가 멤버(형 개체 포인터 및 동기화된 블록 인덱스)에 필요한 메모리 양을 더한 것입니다.
2. 값 유형 필드를 새로 할당된 메모리에 복사합니다.
3. 객체의 주소를 반환합니다. 이때 주소는 객체에 대한 참조이고, 값 유형은 이제 참조 유형으로 변환되었습니다.
이런 방식으로 Add 메서드에 박스형 Point 개체에 대한 참조가 저장됩니다. 박스형 개체는 프로그래머가 처리하거나 시스템 가비지에서 수집할 때까지 힙에 남아 있습니다. 이 시점에서 boxed 값 유형의 수명은 unboxed 값 유형의 수명을 초과합니다.
위의 boxing을 사용하면 배열의 0번째 요소를 꺼내고 싶다면:
Point p = (Point)array[0];
해야 할 일 ArrayList의 요소 0에 대한 참조를 Point 값 유형 p에 넣는 것입니다. 이 목표를 달성하려면 어떻게 구현해야 할까요? 먼저 박스형 Point 객체의 각 Point 필드 주소를 얻습니다. 언박싱을 위한 것입니다. 그런 다음 이러한 필드에 포함된 값은 힙에서 스택 기반 값 유형 인스턴스로 복사됩니다. 언박싱은 본질적으로 객체에 포함된 기본 값 유형에 대한 참조를 얻는 프로세스입니다. 실제로 참조는 박스형 인스턴스의 박스형이 아닌 부분을 가리킵니다. 따라서 박싱과 달리 박싱 해제에서는 메모리의 바이트를 복사할 필요가 없습니다. 그러나 또 다른 요점은 개봉 직후 현장 복사 작업이 발생한다는 것입니다.
따라서 박싱과 언박싱은 프로그램의 속도와 메모리 소모에 부정적인 영향을 미치게 되므로 프로그램이 자동으로 박싱/언박싱 작업을 수행하는 시점에 주의하고, 코드 작성 시 이러한 상황을 피하도록 노력하세요.
언박싱 시 다음 예외 사항에 주의하세요.
1. "박싱된 값 유형 인스턴스에 대한 참조"가 포함된 변수가 null인 경우 NullReferenceException이 발생합니다.
2. 참조가 가리키는 객체가 예상 값 유형의 박스형 인스턴스가 아닌 경우 InvalidCastException이 발생합니다.
예를 들어 다음 코드 조각은

             Int32 x = 5;

            Object o = x;

            Int16 r = (Int16)o;//抛出InvalidCastException异常

unboxing 시 원래 unboxed 값 유형으로만 변환될 수 있기 때문입니다. 위 코드를

             Int32 x = 5;

            Object o = x;

            //Int16 r = (Int16)o;//抛出InvalidCastException异常

            Int16 r = (Int16)(Int32)o;

로 수정하세요. 정답입니다.
박스를 풀고 나면 다음 코드와 같이 필드 복사가 발생합니다.

            //会发生字段复制
            Point p1;

            p1.x = 1;

            p1.y = 2;

            Object o = p1;//装箱,发生复制

            p1 = (Point)o;//拆箱,并将字段从已装箱的实例复制到栈中

다음 코드 세그먼트를 다시 살펴보세요.

            //要改变已装箱的值

            Point p2;

            p2.x = 10;

            p2.y = 20;

            Object o = p2;//装箱

            p2 = (Point)o;//拆箱

            p2.x = 40;//改变栈中变量的值

            o = p2;//再一次装箱,o引用新的已装箱实例

여기서의 목적은 x를 복사하는 것입니다. p2 박싱 후 값이 40으로 변경됩니다. 이렇게 하면 한 번 unboxing하고 필드를 스택에 한 번 복사한 다음 스택에 있는 필드의 값을 변경한 다음 boxing을 한 번 수행해야 합니다. 힙에 새로운 박스형 상자를 생성합니다. 이를 통해 우리는 박싱/언박싱 및 복사가 프로그램 성능에 미치는 영향도 볼 수 있습니다.
몇 가지 박싱 및 언박싱 코드 스니펫을 더 살펴보겠습니다.

            //装箱拆箱演示
            Int32 v = 5;

            Object o = v;

            v = 123;

            Console.WriteLine(v + "," + (Int32)o);

여기서는 세 번의 박싱이 발생했지만

            Object o = v;

            v = 123;

에서는 Console.WriteLine 패킹도 한 번 발생했음을 분명히 알 수 있습니다. , 왜? 여기 WriteLine에는 문자열 유형 매개변수가 있고 모든 사람이 문자열이 참조 유형이라는 것을 알고 있기 때문에 (Int32)o를 여기서 boxing해야 합니다. 여기서도 프로그램에서 문자열을 연결하기 위해 + 기호를 사용하는 문제에 대해 설명합니다. 연결 중에 여러 값 유형이 있으면 여러 박싱 작업이 수행됩니다.
그러나 위의 코드는 다음과 같이 수정할 수 있습니다.

            //修改后
            Console.WriteLine(v.ToString() + "," + o);

이렇게 하면 복싱이 없습니다.
다음 코드를 다시 살펴보세요.

 Int32 v = 5;

            Object o = v;

            v = 123;

            Console.WriteLine(v);

            v = (Int32)o;

            Console.WriteLine(v);

여기서는 단 하나의 boxing, 즉 Object o = v가 발생하고 Console.WriteLine은 int, bool, double 등을 오버로드하기 때문에 여기서는 발생하지 않습니다. .

위 내용은 C# 기본 지식(18) 값 유형의 Boxing 및 Unboxing(1)에 대한 내용이며, 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php. CN)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.