ice rabbit programming

[C#] C#의 자료형2 - Value vs Reference, Nullable 본문

Development/C#

[C#] C#의 자료형2 - Value vs Reference, Nullable

판교토끼 2020. 4. 15. 16:08

지난 자료형 글에서 기본적인 자료형에 대해서 언급하였다.

이번 글에서는 거기서 나아가서 좀 더 자세히 알아보자.

C#의 모든 것은 객체

C#은 기본적으로 모든 것이 객체이다. int, string, 심지어 숫자까지 모두. 모든 것은 System.Object를 상속 받는다.

그러므로 모든 객체는 ToString(), GetType()등의 메서드를 기본적으로 가지고 있다.

10.ToString();

이런 문장이 가능하다는 뜻이다.

Value vs Reference

다른 언어를 해보신 분들은, Call by Value와 Call by Reference 등에 대해서 많이 보았을 것이다. 특히 포인터가 있는 C/C++에서는 포인터나 참조는 Reference, 값은 Value인 것이 중요하다.

C#에서는 struct는 value type이다. value type은 stack에 저장되며 null이 허용되지 않는다.
반면 class는 reference type이다. reference type은 heap에 저장되며 null이 허용된다.

즉, C++에서는 정적할당인지, 동적할당인지에 따라 객체를 사용하는 사람이 저장할 곳을 정했다면, C#에서는 해당 객체의 틀을 만드는 사람이 저장할 곳을 정한다고 할 수 있다.

지난 글의 말미에서 int는 struct이고 string은 class라 했는데, 이런 차이가 있다. 그래서 int는 value로 동작한다. 다만 string의 경우에는 조금 다르게 동작한다.

int num = 1; // num이 stack에 생긴다.
int num2 = num; // num2라는 새로운 value가 stack에 생긴다.

Class Car ...

var c = new Car(); // 새로운 Car 객체가 heap에, c라는 참조가 stack에 생기고 c는 Car 객체를 가리킨다.
var c2 = c; // c2라는 참조가 stack에 생기고 c가 가리키는 Car 객체를 같이 가리킨다.

string s1 = "hi";
s1 = "hello"; // 새로운 string 객체가 생긴다!!!

마지막 문장에서, s1 내의 문자열을 바꾸면 객체가 바뀌게 된다. 왜냐하면 string은 다음과 같이 동작하기 때문이다.

string s1 = "hello"; // String s1 = new String("hello");
s1 = "hi"; // s1 = new String("hi");

 

Nullable(?, ??)

C#에도 NullException이 존재한다. 하지만 Null 값이 들어가는 것을 허용하는 타입이 있다.

일단, 위에서 말했듯이 reference type은 null을 허용한다. 그래서 null에 대한 검사를 따로 진행해야 한다. 만약 null인데 사용하려고 하면 NullReference Exception이 발생한다.
반면 value type은 null을 허용하지 않는다. 그런데 만약 null을 허용하고 싶다면? 예를 들어, 다음을 보자.

int foo(object possible) {
    if(possible.value==1)
    	return 0;
    else if(possible.value==2)
    	return 1;
    else // 그 외에는 틀렸다는 값을 반환하고 싶다면?
    	return null; // int는 null을 허용하지 않으므로 Error 발생
}

현재 foo 메서드는 int를 반환하기 때문에 null을 반환할 수 없다. -1을 틀린 값으로 약속하고 반환할 수는 있다. 하지만 이게 계산 결과가 -1인지, 틀려서 -1인지 알 수 없다.

이럴 때 null을 허용해주는 방법이 있다. Nullable<int>, int? 를 사용하는 것이다. 이는 null을 허용해주는데, int+bool 값이다.

int num1 = null; // Error
int? num2 = null; // ok

?.과 ?[(Elvis Operator)도 있는데, 객체와 배열에서 사용한다.

object?.Go(); // object가 null이면 수행하지 않고 null 반환
arr?[0]; // arr가 null이면 null 반환

이런식으로 객체 내의 멤버나 배열 내의 값에 접근하기 전에 null 검사를 진행하는 것이다(참고로 배열은 reference type이라 null을 허용한다.).

object의 null 검사 연산자는 ?? 도 있다.

void foo(object hi) {
	var hello = hi ?? ""; // hi가 null이 아니면 hi, null이면 ""
}

좌변 값을 검사하여 null이 아니면 좌변 값을, null이면 우변 값을 반환하게 된다.

 

다음 글에서는 Casting과 Boxing&Unboxing에 대해서 다루도록 하겠다.

'Development > C#' 카테고리의 다른 글

[C#] 메소드  (0) 2020.05.04
[C#] 배열(Array)  (0) 2020.04.30
[C#] C#의 자료형3 - Casting과 Boxing  (0) 2020.04.18
[C#] C#의 자료형  (0) 2020.04.10
[C#] C#을 시작하며, .NET Framework  (0) 2020.04.09