티스토리 뷰
이미 MSDN에 아래 링크에 대한 번역이 올라와있지만, 공부를 위해서 한번 더 번역을 해보았습니다.
또한 이해되기 쉽게 문장을 변경하기도 했습니다.
그리고 해당 링크에 나와있는 내용만으로는 알 수 없는 내용을 더 추가해봤습니다.
내용 중에 굵은 글씨로 되어있는 부분은 설명을 추가한 부분입니다.
추가된 설명은 모든 해석 내용 밑에 위치해있습니다.
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing
Boxing and Unboxing - C# Programming Guide
Boxing and Unboxing (C# Programming Guide) In this article --> Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the common language runtime (CLR) boxes a value type, it wraps
docs.microsoft.com
Boxing은 값 형식을 object 타입 혹은 이 값 형식에 의해 구현된 어떤 인터페이스 형식(1) 으로 변환하는 과정이다.
CLR은 값 형식을 box할 때, System.Object 인스턴스 내부에 해당 값을 감싼다.
그리고 그것을 managed heap에 저장한다.
Unboxing은 값 형식을 object로 부터 추출한다.
Boxing은 암묵적이다.
UnBoxing은 명시적이다.
boxing과 unboxing 개념은 어떤 형식의 값이라도 객체처럼 다룰 수 있는 C#의 통일된 형식 시스템의 기초가 된다.
따라오는 예제는 정수형 변수 i가 boxing되고 object o로 배정된다.
int i = 123;
// The following line boxes i.
object o = i;
그러면 오브젝트 o는 unboxing 될 수 있고 정수형 변수 i로 배정된다.
o = 123;
i = (int)o; // unboxing
따르는 예제는 어떻게 C#에서 박싱이 사용되는지 보여준다.
// String.Concat example.
// String.Concat has many versions. Rest the mouse pointer on
// Concat in the following statement to verify that the version
// that is used here takes three object arguments. Both 42 and
// true must be boxed.
Console.WriteLine(String.Concat("Answer", 42, true));
// List example.
// Create a list of objects to hold a heterogeneous collection
// of elements.
List<object> mixedList = new List<object>();
// Add a string element to the list.
mixedList.Add("First Group:");
// Add some integers to the list.
for (int j = 1; j < 5; j++)
{
// Rest the mouse pointer over j to verify that you are adding
// an int to a list of objects. Each element j is boxed when
// you add j to mixedList.
mixedList.Add(j);
}
// Add another string and more integers.
mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
mixedList.Add(j);
}
// Display the elements in the list. Declare the loop variable by
// using var, so that the compiler assigns its type.
foreach (var item in mixedList)
{
// Rest the mouse pointer over item to verify that the elements
// of mixedList are objects.
Console.WriteLine(item);
}
// The following loop sums the squares of the first group of boxed
// integers in mixedList. The list elements are objects, and cannot
// be multiplied or added to the sum until they are unboxed. The
// unboxing must be done explicitly.
var sum = 0;
for (var j = 1; j < 5; j++)
{
// The following statement causes a compiler error: Operator
// '*' cannot be applied to operands of type 'object' and
// 'object'.
//sum += mixedList[j] * mixedList[j]);
// After the list elements are unboxed, the computation does
// not cause a compiler error.
sum += (int)mixedList[j] * (int)mixedList[j];
}
// The sum displayed is 30, the sum of 1 + 4 + 9 + 16.
Console.WriteLine("Sum: " + sum);
// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30
Performance
간단한 할당 과정에서, boxing과 unboxing은 계산적으로 비싼 프로세스이다.
값형식이 box될 때, 새로운 object가 할당되고 구성되어야한다.
더 낮은 단계에서, unboxing에 필요한 형 변환 또한 계산적으로 비싸다.
Boxing
Boxing은 값 형식을 garbage-collected heap에 저장하기 위해서 사용된다.
Boxing은 암묵적으로 값형식을 object 형식 혹은 이 값 형식에 의해 구현된 어떤 인터페이스(1)로 변하는 변환이다.
Boxing 하는 값 형식은 힙에 object 인스턴스를 할당한다. 그리고 value를 새로운 object로 복사한다.
따라오는 값 형식 변수의 정의를 보자.
int i = 123;
따라오는 구문은 암묵적으로 변수 i에 boxing 연산을 적용하는 것이다.
// Boxing copies the value of i into object o.
object o = i;
이 구문의 결과는 힙에 있는 int 타입의 값을 참조하는 object 참조 o를 스택에 만들 것이다.
이 값은 변수 i에 배정된 value 형식 값의 복사본이다.
i와 o의 차이점은 따라오는 boxing 변환의 이미지가 보여준다.
또한 다음 예제와 같이 명시적으로 boxing을 수행하는 것이 가능하다는 것이다.
하지만 명시적인 boxing은 요구되지 않는다. ( = 명시하지 않아도 에러가 아님. )
int i = 123;
object o = (object)i; // explicit boxing
Description
정수형 변수 i를 object o로 boxing을 사용함으로써 변환하는 예제이다.
그래서 변수 i에 저장된 값은 123에서 456로 변한다.
이 예제는 원래 값 형식과 box된 object가 분리된 메모리 영역을 사용한다는 것과 다른 값을 저장할 수 있다는 것을 보여준다.
Example
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
Unboxing은 object 형식에서 값 형식으로 혹은 인터페이스 형식에서 인터페이스를 구현한 값 형식으로 변하는 명시적인 변환이다.
unboxing 연산은 다음으로 구성되어있다.
- object 인스턴스가 주어진 값 형식의 box된 값인지 확실하게 확인하는 것 ( = 형식이 일치하는지 확인 )
- 값 형식 변수 인스턴스로부터 값을 복사하는 것 ( = box되어 있는 인스턴스로부터 해당 변수에 복사 )
따라오는 구문은 boxing과 unboxing 연산을 둘다 보여준다.
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
따라오는 그림은 이전의 구문의 결과를 보여준다.
값 형식의 unboxing을 런타임 동안 성공하기 위해서, unbox된 아이템은 반드시 이전에 값 형식을 box함으로써 생성된 object로 참조가 되어야한다.
null을 unbox하는 것을 시도하는 것은 NullReferenceException을 발생시킨다.
변환이 불가능한 value 타입으로 참조를 unbox하는 것을 시도하는 것은 InvalidCastException을 발생시킨다.
Example
따라오는 예제는 유효하지 않은 unboxing의 예를 보여준다. 그리고 InvalidCaseException을 결과를 보여준다.
try 그리고 catch 를 사용하여 에러가 발생할 때, 에러 메세지가 보여진다.
class TestUnboxing
{
static void Main()
{
int i = 123;
object o = i; // implicit boxing
try
{
int j = (short)o; // attempt to unbox
System.Console.WriteLine("Unboxing OK.");
}
catch (System.InvalidCastException e)
{
System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
}
}
}
이 프로그램은 다음과 같은 결과를 내보낸다.
Specified cast is not valid. Error: Incorrect unboxing.
(short) -> (int) 로 변경하면 변환이 일어난다.
그리고 다음과 같은 결과를 얻게 된다.
Unboxing OK.
추가 내용
1. 이 값 형식에 의해 구현된 어떤 인터페이스 형식
나는 이와 같은 문장을 보고 임의의 인터페이스가 존재하는데, 그것이 이 값 형식을 상속받은 것이라고 생각했다.
하지만 이것은 논리적으로 이상했다. 왜냐하면 우선 문법적으로도 인터페이스는 class나 struct를 상속받을 수 없었기 때문이다.
그래서 여러 자료를 찾다보니 "해당 값 형식을 구현한 어떤 인터페이스 형식"이라는 표현이 맞다는 것을 알게 되었다.
즉, 전체 문장에서 보자면 어떤 값 형식에서 해당 값 형식의 부모 인터페이스 형식으로 변환된다는 것이다.
( 한국어 번역이 되어있는 공식 설명서에도 "이 값 형식에서 구현된 임의의 인터페이스 형식" 이라고 번역되어 있다. 이 것 또한 마치 값 형식이 인터페이스를 구현한 것처럼 쓰여져있다. )
그렇다면, 이러한 상황은 무엇일까? 따라오는 코드를 통해 살펴보자.
1. 인터페이스로 boxing이 되는 경우
namespace TestBoxing
{
interface IScoreList
{
void Add(int amount);
}
struct ScoreList : IScoreList
{
int score;
public ScoreList(int score)
{
this.score = score;
}
void IScoreList.Add(int amount)
{
score += amount;
}
public void Add(int amount)
{
score += amount;
}
public void Print()
{
Console.WriteLine($"Score: {score}");
}
}
public class TestBoxing
{
public static void Main(string[] args)
{
ScoreList vScoreList = new ScoreList(100);
vScoreList.Add(50);
vScoreList.Print();
IScoreList iScoreList = vScoreList;
iScoreList.Add(50);
vScoreList.Print();
ScoreList newVScoreList = (ScoreList)iScoreList;
newVScoreList.Print();
}
}
}
다음으로는 이것의 출력 결과이다.
1번째줄의 출력 결과는 100으로 지정된 점수에 50을 추가하여 출력했으니 150이 나오게 된다.
여기까지는 문제될 것이 없다.
하지만 두번째 결과를 보면, vScoreList를 할당해 생성된 iScoreList에 50을 더했음에도 불구하고 150이 출력되는 것을 알 수 있다.
이것을 통해 두 변수는 서로 완전히 다른 값을 할당받고 있다고 할 수 있다.
이렇게 된 이유는 iScoreList에 vScoreList에 할당될 때 boxing이 일어났기 때문이다.
또한 3번째 줄을 보면 unboxing이 일어나 newVScoreList에 iScoreList의 값이 저장되었음을 확인할 수 있다.
이것이 임의의 값 형식이 해당 값 형식을 구현한 인터페이스로 변환되는 과정이다.
'개발 일기' 카테고리의 다른 글
[개발] 예외 처리에 대한 정리 (0) | 2020.12.15 |
---|---|
[Windows Terminal] 터미널 시작 시 기본 프로그램 설정법 (0) | 2020.08.05 |
[C#] BinaryWriter 를 이용하여 문자열 저장 시 생기는 현상 (0) | 2020.06.03 |
[Windows 팁] Windows terminal 테마 변경과 추가, 키 바인딩 설정 (0) | 2020.06.02 |
[Visual Studio] Visual Studio 팁 정리 ( 단축키, 간단한 팁 등등 ) (0) | 2020.05.18 |