[C# 학습 노트] 포장 및 언박싱

여기에 이미지 설명을 삽입하세요.


포장 및 개봉

참조형 객체에 대해 이야기할 때 우리는 그것이 전능하다고 말하지만, 어디가 전능하다고는 말하지 않습니다.

각 변수 유형에 대해 ToString, GetHashCode, Equals 및 GetType 메소드를 제공하는 객체 외에도 object는 모든 유형의 상위 클래스이며 모든 변수 유형을 object 로 변환할 수 있습니다 .

한편으로 객체 유형을 사용하면 명시적으로~로 변환하다모든 유형(박싱 또는 언박싱이 발생하지 않는 경우):

object arr=new int[10];
int[] ar=(int[])arr;

object arr=new int[10];
int[] ar=arr as int[]; // as强转object类型为int数组

반면에 값 유형을 변환합니다.다음으로 변환됨객체( 참조 유형 ) 의 프로세스를 박싱(boxing )이라고 하며 , 박싱된 객체를 다시 값 유형으로 변환하는 프로세스를 언 박싱( unboxing) 이라고 합니다 .

다음 예제에서는 정수 변수 i를 상자에 넣고 이를 객체 o에 할당합니다.

int i = 123;
// The following line boxes i.
object o = i; //隐式装箱

그런 다음 객체 o를 unboxing하고 정수 변수 i에 할당할 수 있습니다.

o = 123;
i = (int)o;  // unboxing

다음은 객체 목록을 사용하여 다양한 값 유형의 데이터를 저장하고 각각 작업하는 방법을 보여주는 공식적인 예입니다.

Console.WriteLine(String.Concat("Answer", 42, true));
List<object> mixedList = new List<object>();
mixedList.Add("First Group:");

for (int j = 1; j < 5; j++)
{
    
    
    mixedList.Add(j);
}

mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
    
    
    mixedList.Add(j);
}

foreach (var item in mixedList)
{
    
    
    Console.WriteLine(item);
}

var sum = 0;
for (var j = 1; j < 5; j++)
{
    
    
    sum += (int)mixedList[j] * (int)mixedList[j];
     // 注意在进行数值运算的时候不能用object直接运算,应当拆箱为原类型再计算
}
Console.WriteLine("Sum: " + sum);

// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30

성능 소모

단순한 할당에 비해 박싱 및 언박싱 과정에는 많은 계산이 필요합니다. 값 유형을 박싱할 때 새 개체를 할당하고 생성해야 합니다. 언박싱에 필요한 캐스트 역시 비록 그 정도는 낮지만 계산 집약적입니다.

실제로 프로그래밍에서는 꼭 필요한 경우가 아니면 박싱(boxing)과 언박싱(unboxing)을 피해야 합니다.

변수가 상자에서 참조된 후 unboxing되어야 하는 경우 새 변수를 직접 할당하는 것이 좋습니다. 값 유형을 boxing할 때 새로운 객체를 생성해야 하며 이는 단순한 참조 할당보다 시간이 더 오래 걸릴 수 있기 때문입니다. .20번. 언박싱 프로세스는 할당 작업보다 최대 4배의 시간이 걸릴 수 있습니다.

변수나 메서드에 박싱과 언박싱이 자주 필요한 경우 프로그래밍 자체에 문제가 있음을 나타냅니다. 예를 들어, 모든 입력 유형을 허용하는 함수가 정의되어 있지만 모든 입력 값 유형은 메서드 내에서 객체 유형으로 강제 변환됩니다. 위의 예에서 정의한 대로 List<object>값을 저장할 때마다 값이 boxing됩니다. 실제로 제네릭을 정의하는 것이 List<T>더 나은 선택입니다. 어떤 값 유형의 변수를 받아야 하는 경우 다른 유형에 대한 객체 박싱 대신 제네릭을 사용해야 합니다. 예를 들어 다음 예에서는 전자가 확실히 더 좋습니다.

    public class Stack<T>
    {
    
    
        List<T> a = new List<T>();
    }
    public class Stack
    {
    
    
        List<object> b = new List<object>();
    }

포장

Boxing은 가비지 수집 힙에 값 유형을 저장하는 데 사용됩니다. 박싱은 값 유형을 객체 유형 또는 이 값 유형에 의해 구현된 인터페이스 유형으로 암시적으로 변환하는 것입니다. 값 유형을 박싱하면 힙에 개체 인스턴스가 할당되고 해당 값이 새 개체에 복사됩니다.

이 문의 결과는 스택에 객체 참조 o를 생성하고 힙에 int 유형의 값을 생성하는 것입니다. 값은 변수 i에 할당된 값 유형 값의 복사본입니다. 다음 비닝된 변환 도표는 두 변수 i와 o 사이의 차이를 보여줍니다.

여기에 이미지 설명을 삽입하세요.
(위 그림에서 볼 수 있듯이 원래 i는 값 유형으로 스택에 저장되고, 객체는 참조 유형으로 힙에 저장됩니다. boxing 작업을 수행할 때 한편으로는 boxing 유형은 값 유형은 On the heap에 기록되며, 힙에 있는 객체의 값은 123으로 할당되고, 스택에는 참조 o가 생성되어 힙에 있는 객체를 참조합니다.)

즉, 박싱 중에 객체는 주소 자체가 아닌 i 값만 복사합니다. 값 유형이 할당되면 주소가 다시 생성되고 참조 값 유형은 신뢰할 수 없기 때문에 이해하기 쉽습니다.

class TestBoxing
{
    
    
    static void Main()
    {
    
    
        int i = 123;
        // Boxing copies the value of i into object o.
        object o = i;
        // Change the value of i.
        i = 456;
        // The change in i doesn't affect the value stored in o.
        System.Console.WriteLine("The value-type value = {0}", i);
        System.Console.WriteLine("The object-type value = {0}", o);
    }
}
/* Output:
    The value-type value = 456
    The object-type value = 123
*/

언박싱

Unboxing은 객체 유형에서 값 유형으로 또는 인터페이스 유형에서 인터페이스를 구현하는 값 유형으로 명시적으로 변환하는 것입니다. 개봉 작업에는 다음이 포함됩니다.

  • 객체 인스턴스를 검사하여 주어진 값 유형의 박스형 값인지 확인합니다.

  • 인스턴스의 값을 값 유형 변수에 복사합니다.
    여기에 이미지 설명을 삽입하세요.

언박싱 프로세스에서 볼 수 있듯이 실제로 언박싱할 때 먼저 언박싱 항목 o가 개체에 대한 참조인지 여부를 결정합니다. 그런 다음 언박싱 유형이 객체의 박싱 유형인지 판단하고 최종적으로 언박싱된 값을 스택에 다시 생성합니다.

또한 언박싱에는 암시적 변환도 있지만 언박싱 항목 유형은 다음 예와 같이 동일해야 합니다.

float t =(int) o; //拆箱项正确,拆出的int可直接隐式转换为folat
int t =(float) o; //拆箱项错误

위의 원칙에 따르면, 박스형 개체의 값을 변경하려는 경우 유일한 방법은 먼저 해당 개체를 unboxing한 다음 reboxing하는 것입니다.


var, object, 동적, <T> 비교

이 네 가지 메서드는 비슷해 보이고 어떤 유형이든 수신할 수 있지만 실제로는 매우 다릅니다.

~였다

var의 원리는 컴파일러를 기반으로 하며, var를 사용하여 변수 유형을 정의할 때 지역 변수 에만 사용할 수 있으며 컴파일러가 초기화 표현식에서 변수 유형을 유추할 수 있습니다.

var a = 12;
a.IndexOf("1", 0, 2); //报错,编译器已经推测出a是int类型了,不能使用string的方法

var의 일반적인 용도는 생성자 호출 표현식입니다. 함수에 있는 일부 지역 변수의 할당 유형을 알 수 없는 경우 var를 사용하여 이를 받는 것이 안전합니다. var 유형을 사용하는 것이 가장 편리하고 캐주얼해야 합니다.

물체

객체는 어떠한 유형으로도 변환이 가능하지만 boxing을 통해 값 유형 변환도 받을 수 있습니다. 장점은 객체 유형을 함수의 반환 유형으로 사용하거나 함수의 입력 매개변수에서 객체 클래스를 정의할 수 있다는 것입니다. 하지만 장점도 단점이기도 합니다. 객체 매개변수가 함수에 정의되어 있다고 가정하면 다른 사람이 함수를 사용할 때 무엇이 ​​전달될지 누가 알겠습니까? 상황은 점점 더 복잡해집니다. 제네릭을 사용하거나 매개변수 유형을 지정하고 이를 함수 안에 넣습니다.

따라서 유형 변환 시에는 객체만 사용하는 것이 좋습니다. 함수에서 변경 가능한 유형을 정의하고 박싱 및 언박싱이 발생하지 않도록 하려면 <T>를 사용하세요.

<T> 제네릭

제네릭은 변수를 제외한 모든 것을 정의할 수 있습니다. 자신만의 일반 인터페이스, 일반 클래스, 일반 메서드, 일반 이벤트 및 일반 대리자를 만들 수 있습니다. 일반 클래스는 특정 데이터 유형의 메서드에 액세스하도록 제한될 수도 있습니다.

제네릭의 간단한 예는 컬렉션 클래스에서 제공됩니다 List<T>. 이것이 얼마나 유용한지는 말할 필요도 없습니다.

다양한 클래스나 메소드에서 임의의 유형을 얻으려면 제네릭을 사용하십시오. 제네릭에는 boxing이나 unboxing이 필요하지 않습니다. 제네릭을 대체품으로 이해하고 제네릭 매개변수를 사용할 때 특정 유형으로 대체할 수 있습니다. 이 프로세스는 컴파일 중에 수행됩니다. 제네릭을 사용하면 컴파일러는 여전히 유형 오류를 감지할 수 있습니다.

동적

동적 자체도 객체입니다. 대부분의 경우 동적 유형은 객체 유형과 유사하게 동작합니다. 특히 Null이 아닌 모든 표현식은 동적 유형으로 변환될 수 있습니다. 동적 유형은 컴파일러가 동적 유형을 포함하는 표현식에 대해 구문 분석이나 유형 확인 작업을 수행하지 않는다는 점에서 객체와 다릅니다. 컴파일러는 작업에 대한 정보를 함께 패키지화한 다음 런타임 시 작업을 평가하는 데 사용합니다. 이 과정에서 동적 유형 변수는 객체 유형 변수로 컴파일됩니다. 따라서 동적 유형은 런타임이 아닌 컴파일 타임에만 존재합니다.

장점은 대부분의 스크립팅 언어처럼 동적 변수라는 점인데, 한편으로는 특정 동적 사용에 편리하고 다른 한편으로는 스크립팅 언어와도 연결할 수 있습니다.

그러나 단점도 분명하며 때로는 이를 사용하여 작성된 프로그램을 디버깅하는 것이 그리 간단하지 않을 수 있습니다. 동시에, 동적을 함수의 매개변수나 반환값으로 사용하지 마세요. 객체보다 더 귀찮은 결과만 가져올 뿐입니다.

추천

출처blog.csdn.net/milu_ELK/article/details/132097436