[c#] C #에서 권투 및 언 박싱이 필요한 이유는 무엇입니까?

C #에서 권투 및 언 박싱이 필요한 이유는 무엇입니까?

복싱과 언 박싱이 무엇인지 알고 있지만 실제 사용을 이해할 수는 없습니다. 왜 그리고 어디서 사용해야합니까?

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing



답변

통합 형식 시스템을 보유하고 값 형식이 참조 형식이 기본 데이터를 나타내는 방식과 기본 데이터를 완전히 다르게 표현할 수 있도록 허용 (예 : inta는 참조와 완전히 다른 32 비트의 버킷 임) 유형).

이것을 이렇게 생각하십시오. o유형 의 변수 가 object있습니다. 그리고 지금 당신은 int그것을 가지고 o있습니다. o는 어딘가에 대한 참조이며, 어딘가에 int대한 참조가 아닙니다 (결국 숫자 일뿐입니다). 그래서 당신이하는 일은 이것입니다 :를 object저장할 수있는 새로운 것을 만들고 int그 객체에 대한 참조를에 할당합니다 o. 우리는이 과정을 “복싱”이라고 부릅니다.

따라서 통합 유형 시스템에 신경 쓰지 않는다면 (즉, 참조 유형과 값 유형이 매우 다른 표현을 가지고 있고 두 가지를 “표현”하는 일반적인 방법을 원하지 않는 경우) 권투가 필요하지 않습니다. int기본 값 을 나타내는 것에 신경 쓰지 않는다면 (즉, int참조 유형이기도하고 기본 값에 대한 참조를 저장하는 경우) 권투가 필요하지 않습니다.

어디서 사용해야합니까?

예를 들어, 이전 컬렉션 유형 ArrayListobjects 만 먹는다 . 즉, 어딘가에 사는 것에 대한 참조 만 저장합니다. 권투가 없으면 int그러한 컬렉션에 넣을 수 없습니다 . 그러나 권투로 할 수 있습니다.

이제 제네릭 시대에는 실제로 이것이 필요하지 않으며 일반적으로 문제에 대해 생각하지 않고 즐겁게 갈 수 있습니다. 그러나 알아야 할 몇 가지주의 사항이 있습니다.

이것은 맞습니다 :

double e = 2.718281828459045;
int ee = (int)e;

이것은 아니다:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

대신이 작업을 수행해야합니다.

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

먼저 double( (double)o) 을 명시 적으로 개봉 한 다음으로 캐스팅해야합니다 int.

다음의 결과는 무엇입니까?

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

다음 문장으로 넘어 가기 전에 잠시 생각해보십시오.

당신이 말한 경우 TrueFalse큰! 무엇을 기다립니다? 이는 ==참조 유형에서 기본 값이 아닌 참조가 동일한 지 확인하는 참조 평등을 사용 하기 때문 입니다. 이것은 위험한 실수입니다. 아마도 더 미묘한

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

또한 인쇄합니다 False!

더 나은 말 :

Console.WriteLine(o1.Equals(o2));

그러면 고맙게도 인쇄 할 것 True입니다.

마지막 미묘함 :

[struct|class] Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

출력은 무엇입니까? 때에 따라 다르지! 경우 PointA는 struct다음 출력은 1있지만, 경우에 Point있는 class다음 출력은 2! 복싱 변환은 동작의 차이를 설명하는 복싱되는 값의 복사본을 만듭니다.


답변

.NET 프레임 워크에는 값 유형과 참조 유형의 두 가지 유형이 있습니다. 이것은 OO 언어에서 비교적 일반적입니다.

객체 지향 언어의 중요한 기능 중 하나는 유형에 관계없이 인스턴스를 처리 할 수 ​​있다는 것입니다. 이것을 다형성 이라고합니다 . 우리는 다형성을 이용하기를 원하지만, 두 종류의 다른 유형이 있기 때문에, 그것들을 하나로 모으는 방법이 있어야 하나 또는 다른 방법으로 처리 할 수 ​​있습니다.

이제 옛날 (Microsoft.NET의 1.0)로 돌아가서이 새로운 제네릭 훌라 바루는 없었습니다. 값 유형과 참조 유형을 처리 할 수있는 단일 인수가있는 메소드를 작성할 수 없습니다. 그것은 다형성의 위반입니다. 따라서 복싱은 가치 유형을 객체로 강제하는 수단으로 채택되었습니다.

이것이 가능하지 않다면, 프레임 워크는 다른 유형의 유형을 수용하는 유일한 목적을 가진 메소드와 클래스로 어지럽 힐 것입니다. 뿐만 아니라 값 유형은 공통 유형 조상을 공유하지 않으므로 각 값 유형 (비트, 바이트, int16, int32 등)에 대해 서로 다른 메서드 오버로드를 가져야합니다.

권투는 이것이 일어나지 않도록 막았습니다. 그리고 이것이 영국이 박싱 데이를 축하하는 이유입니다.


답변

이를 이해하는 가장 좋은 방법은 C #이 구축 한 하위 프로그래밍 언어를 보는 것입니다.

C와 같은 가장 낮은 수준의 언어에서 모든 변수는 스택으로 이동합니다. 변수를 선언 할 때마다 스택으로 이동합니다. 이들은 bool, byte, 32-bit int, 32-bit uint 등과 같은 기본 값일 수 있습니다. Stack은 단순하고 빠릅니다. 변수가 추가됨에 따라 변수가 하나 위로 올라가므로 처음 선언하는 것은 0x00, 다음은 0x01, 다음은 RAM의 0x02 등입니다. 또한 변수는 종종 컴파일시 사전 주소 지정됩니다. 프로그램을 실행하기 전에 주소를 알 수 있습니다.

다음 단계에서는 C ++과 같이 힙이라는 두 번째 메모리 구조가 도입되었습니다. 여전히 스택에 살고 있지만 Pointers 라는 특수 정수 를 스택에 추가하여 Object의 첫 번째 바이트에 대한 메모리 주소를 저장하고 Object가 힙에 있습니다. 힙은 일종의 혼란스럽고 유지 보수 비용이 많이 듭니다. 스택 변수와 달리 프로그램이 실행될 때 선형으로 쌓여서 쌓이지 않습니다. 그들은 특정한 순서로왔다 갔다 할 수 있고, 자라고 줄어들 수 있습니다.

포인터 다루기가 어렵습니다. 메모리 누수, 버퍼 오버런 및 좌절의 원인입니다. 구조에 C #.

더 높은 수준의 C #에서는 포인터에 대해 생각할 필요가 없습니다. .Net 프레임 워크 (C ++로 작성)는이를 고려하여이를 객체에 대한 참조로 제공하고 성능을 위해 더 간단한 값을 저장할 수 있도록합니다. bools, bytes 및 int와 같은 Value Types. 후드 아래에서 클래스를 인스턴스화하는 객체와 물건은 값 비싼 메모리 관리 힙을 사용하고 값 유형은 저수준 C-초고속과 동일한 스택에 저장됩니다.

코더의 관점에서 근본적으로 다른 두 가지 메모리 개념 (및 저장 전략) 간의 상호 작용을 간단하게 유지하기 위해 언제라도 Value Types를 박스로 만들 수 있습니다. 복싱은 스택에서 값을 복사하여 객체를 넣고 힙에 배치합니다. 더 비싸지 만 참조 세계와의 유동적 상호 작용입니다. 다른 답변에서 지적했듯이 예를 들어 다음과 같이 말할 때 발생합니다.

bool b = false; // Cheap, on Stack
object o = b; // Legal, easy to code, but complex - Boxing!
bool b2 = (bool)o; // Unboxing!

Boxing의 장점에 대한 강력한 설명은 null을 확인하는 것입니다.

if (b == null) // Will not compile - bools can't be null
if (o == null) // Will compile and always return false

우리의 객체 o는 기술적으로 스택의 주소로서 힙에 복사 된 bool b의 복사본을 가리 킵니다. bool이 Boxed되어 있기 때문에 o를 null로 확인할 수 있습니다.

int / bool / what을 인수로 전달하기 위해 필요한 경우가 아니면 권투를 피해야합니다. .Net에는 여전히 값 유형을 객체로 전달해야하며 (권투가 필요) 기본 구조가 있지만 대부분 상자에 넣을 필요는 없습니다.

복싱이 필요한 역사적 C # 구조의 전체 목록은 피해야합니다.

  • 이벤트 시스템 순진한 경쟁 조건을 가지고 있으며 비동기를 지원하지 않습니다. 권투 문제에 추가하고 아마 피해야합니다. (예를 들어 Generics를 사용하는 비동기 이벤트 시스템으로 대체 할 수 있습니다.)

  • 이전 스레딩 및 타이머 모델은 매개 변수에 Box를 강제했지만 async / await로 대체되었으며 훨씬 깨끗하고 효율적입니다.

  • .Net 1.1 컬렉션은 제네릭보다 먼저 나왔기 때문에 권투에 전적으로 의존했습니다. 이들은 여전히 ​​System.Collections에서 발로 움직입니다. 새로운 코드에서, 당신은 권투를 피하는 것 외에도 강력한 타입 안전성을 제공 하는 System.Collections.Generic의 Collections를 사용해야합니다 .

복싱을 강제하는 위의 역사적 문제를 처리해야하는 경우가 아니라면 나중에 Value Box를 선언하거나 전달하지 말아야합니다.

아래 미카엘의 제안 :

이 작업을 수행

using System.Collections.Generic;

var employeeCount = 5;
var list = new List<int>(10);

이거 말고

using System.Collections;

Int32 employeeCount = 5;
var list = new ArrayList(10);

최신 정보

이 답변은 원래 Int32, Bool 등이 boxing을 제안했습니다. 실제로 값 유형의 간단한 별칭 일 때입니다. 즉, .Net에는 Bool, Int32, String 및 C #과 같은 유형이 있으며 기능상의 차이없이 bool, int, string으로 별칭을 지정합니다.


답변

복싱은 실제로 사용하는 것이 아닙니다. 런타임에서 사용하는 것이기 때문에 필요할 때 동일한 방식으로 참조 및 값 유형을 처리 할 수 ​​있습니다. 예를 들어, ArrayList를 사용하여 정수 목록을 보유한 경우 ArrayList의 객체 유형 슬롯에 맞게 정수가 상자로 표시됩니다.

이제 일반 컬렉션을 사용하면 거의 사라집니다. 을 만들면 List<int>권투가 수행되지 않습니다 List<int>. 정수를 직접 보유 할 수 있습니다.


답변

복싱 및 Unboxing은 특히 값 형식 개체를 참조 형식으로 처리하는 데 사용됩니다. 실제 값을 관리되는 힙으로 이동하고 참조로 값에 액세스합니다.

boxing과 unboxing이 없으면 절대 참조로 값 유형을 전달할 수 없습니다. 즉, 값 유형을 Object 인스턴스로 전달할 수 없습니다.


답변

내가 뭔가를 풀어야했던 마지막 장소는 데이터베이스에서 일부 데이터를 검색하는 코드를 작성할 때였습니다 ( LINQ to SQL 사용하지 않고 일반 오래된 ADO.NET ).

int myIntValue = (int)reader["MyIntValue"];

기본적으로 제네릭 이전의 이전 API로 작업하는 경우 복싱이 발생합니다. 그 외에는 일반적이지 않습니다.


답변

복싱은 객체로 매개 변수를 필요로하는 함수가있을 때 필요하지만, 전달해야하는 다른 값 유형이있는 경우, 함수에 전달하기 전에 먼저 값 유형을 객체 데이터 유형으로 변환해야합니다.

나는 그것이 사실이라고 생각하지 않습니다, 대신 이것을 시도하십시오 :

class Program
    {
        static void Main(string[] args)
        {
            int x = 4;
            test(x);
        }

        static void test(object o)
        {
            Console.WriteLine(o.ToString());
        }
    }

그것은 잘 작동합니다, 나는 권투 / unboxing을 사용하지 않았습니다. (컴파일러가 그것을 배후에서 수행하지 않는 한?)