C#에서 구조체와 클래스는 의미가 조금 다르다.
C++에서 클래스와 구조체는 근본적으로 차이가 없다. 다만, C++에서는 관습적으로 구조체는 액세스 지정자가 public이며, 클래스는 private라는 점만이 다를 뿐, 두 데이터형은 동일하다.
하지만, C#에서는 이 둘은 차이가 있다. 즉, 근본적으로 어떤 타입의 데이터이냐의 문제인데, 스택과 힙 영역 어디에나 데이터를 생성할 수 있는 C/C++과 달리, C#에서 클래스는 항상 힙 영역에 생성된다. 하지만, 구조체는 항상 스택에만 생성되는 것은 아니다. 구조체 역시 힙에 생성할 수 있다. 예를 들어, 구조체 Dummy가 선언되었다고 했을 때 Dummy dummy = new Dummy();라는 문장은, 암묵적으로 디폴트 생성자를 사용하여 힙에 Dummy를 생성한다. 스택이나 힙, 어느 곳에 생성되느냐에 따라 구조체와 클래스가 구별되는 것은 아니다.
문제는 이들이 어떤 방식으로 데이터가 전달되느냐 하는 것이다. C#에서 클래스는 항상 참조 타입이며, 구조체는 값 타입이 된다. 다시 말해, 클래스는 해당 메모리의 어드레스를 간접 참조하여 데이터에 접근하는 반면, 구조체는 이런 과정 없이 바로 데이터에 접근한다는 의미이다. 이 말은, 결국 C#에서 구조체는 동적으로 타입을 판별할 수 없다는 말이며, 단지 데이터의 묶음으로만 사용해야 한다는 말이 된다. C#에서는, 구조체 데이터가 스택에 생성되는지, 힙에 생성되는지에 따라 파악하는 것이 아니라, 이것이 어떤 타입으로 값이 파악되느냐 하는 문제인 것이다.
예를 들어, 다음과 같은 코드를 보자.
int a = 5;
int b = a;
a = 10;
a와 b가 값 타입이 아니라 참조 타입이라면, 이것은 어떻게 되는가? b 역시 10이 되어버린다! 이것을 방지하기 위해 대부분의 기본형들이 클래스가 아닌 구조체로 정의되어 있는 이유이다. 구조체는 참조로서 값이 전달되지 않는다.
하지만, 의문은 완전히 해결된 것은 아니다. 예를 들어, System.Drawing.Point는 구조체임에도 불구하고, Point.X에 값을 할당하려고 하면 Use of unassigned local variable라는 컴파일러 에러(CS0165)를 낸다. 왜 이럴까? 시험 삼아 구조체를 하나 만들고, 여기에 public 변수를 하나 선언한 뒤 외부에서 이 값을 변경한다고 했을때, 아무런 에러를 내지 않는다. 하지만, 프로퍼티를 하나 설정하고, 이 프로퍼티에서 set { variable = value; }와 같이 선언하고, 이 프로퍼티에 접근하면 위와 동일한 에러를 만날 수 있다. 즉, 내부에서 쓰인 public 변수가 초기화되어 있지 않은 상태에서 value 값을 넣어주려고 했기 때문에 에러가 발생한다. 이러한 논리적인 에러를 막기 위해, C#에서의 Point 구조체는 반드시 생성자를 통해서 생성하도록 되어 있으며, 생성자를 통해 생성된 Point 구조체는 논리적으로 잘못된 X, Y값이 설정되는 것을 방지한다.
즉, 이와 같은 예제는 구조체라고 해서 스택에만 생성되는 것이 아니며, 힙에도 생성될 수 있다는 것을 보여주는 사례라 할 수 있다. 중요한 것은, 구조체는 값 타입이며, 클래스는 참조 타입으로 전달된다는 것이다. 그리고, 구조체는 기본 생성자를 가질 수 없으며, 스택에 생성될 경우에는 값이 초기화되지 않는다. new로 생성된 구조체는 값이 초기화된다. 값을 초기화하지 않고 사용한다면 컴파일 에러(CS0170)가 발생할 것이다.
C++에서 클래스와 구조체는 근본적으로 차이가 없다. 다만, C++에서는 관습적으로 구조체는 액세스 지정자가 public이며, 클래스는 private라는 점만이 다를 뿐, 두 데이터형은 동일하다.
하지만, C#에서는 이 둘은 차이가 있다. 즉, 근본적으로 어떤 타입의 데이터이냐의 문제인데, 스택과 힙 영역 어디에나 데이터를 생성할 수 있는 C/C++과 달리, C#에서 클래스는 항상 힙 영역에 생성된다. 하지만, 구조체는 항상 스택에만 생성되는 것은 아니다. 구조체 역시 힙에 생성할 수 있다. 예를 들어, 구조체 Dummy가 선언되었다고 했을 때 Dummy dummy = new Dummy();라는 문장은, 암묵적으로 디폴트 생성자를 사용하여 힙에 Dummy를 생성한다. 스택이나 힙, 어느 곳에 생성되느냐에 따라 구조체와 클래스가 구별되는 것은 아니다.
문제는 이들이 어떤 방식으로 데이터가 전달되느냐 하는 것이다. C#에서 클래스는 항상 참조 타입이며, 구조체는 값 타입이 된다. 다시 말해, 클래스는 해당 메모리의 어드레스를 간접 참조하여 데이터에 접근하는 반면, 구조체는 이런 과정 없이 바로 데이터에 접근한다는 의미이다. 이 말은, 결국 C#에서 구조체는 동적으로 타입을 판별할 수 없다는 말이며, 단지 데이터의 묶음으로만 사용해야 한다는 말이 된다. C#에서는, 구조체 데이터가 스택에 생성되는지, 힙에 생성되는지에 따라 파악하는 것이 아니라, 이것이 어떤 타입으로 값이 파악되느냐 하는 문제인 것이다.
예를 들어, 다음과 같은 코드를 보자.
int a = 5;
int b = a;
a = 10;
a와 b가 값 타입이 아니라 참조 타입이라면, 이것은 어떻게 되는가? b 역시 10이 되어버린다! 이것을 방지하기 위해 대부분의 기본형들이 클래스가 아닌 구조체로 정의되어 있는 이유이다. 구조체는 참조로서 값이 전달되지 않는다.
하지만, 의문은 완전히 해결된 것은 아니다. 예를 들어, System.Drawing.Point는 구조체임에도 불구하고, Point.X에 값을 할당하려고 하면 Use of unassigned local variable라는 컴파일러 에러(CS0165)를 낸다. 왜 이럴까? 시험 삼아 구조체를 하나 만들고, 여기에 public 변수를 하나 선언한 뒤 외부에서 이 값을 변경한다고 했을때, 아무런 에러를 내지 않는다. 하지만, 프로퍼티를 하나 설정하고, 이 프로퍼티에서 set { variable = value; }와 같이 선언하고, 이 프로퍼티에 접근하면 위와 동일한 에러를 만날 수 있다. 즉, 내부에서 쓰인 public 변수가 초기화되어 있지 않은 상태에서 value 값을 넣어주려고 했기 때문에 에러가 발생한다. 이러한 논리적인 에러를 막기 위해, C#에서의 Point 구조체는 반드시 생성자를 통해서 생성하도록 되어 있으며, 생성자를 통해 생성된 Point 구조체는 논리적으로 잘못된 X, Y값이 설정되는 것을 방지한다.
즉, 이와 같은 예제는 구조체라고 해서 스택에만 생성되는 것이 아니며, 힙에도 생성될 수 있다는 것을 보여주는 사례라 할 수 있다. 중요한 것은, 구조체는 값 타입이며, 클래스는 참조 타입으로 전달된다는 것이다. 그리고, 구조체는 기본 생성자를 가질 수 없으며, 스택에 생성될 경우에는 값이 초기화되지 않는다. new로 생성된 구조체는 값이 초기화된다. 값을 초기화하지 않고 사용한다면 컴파일 에러(CS0170)가 발생할 것이다.