2022. 3. 21. 21:20ㆍLanguages/C#
구조체 (Structure)
C#의 복합 데이터 형식에는 클래스 말고도 구조체가 있다. C 언어에서 봤던 그 구조체가 맞긴 하지만 여러모로 다른 점이 있다. 클래스처럼 필드와 메소드를 가질 수 있는 점에서 클래스와 구조체 둘은 서로 많이 비슷해 보인다.
struct MyStructure
{
public int data;
public void Method()
{
...
}
}
클래스와 달리 은닉성을 비롯한 객체지향의 원칙을 강하게 적용하지 않는 편이라, public으로 필드를 사용해서 주로 쓴다.
그렇다면, 구조체와 클래스의 차이점은 뭘까?
특징 | 클래스 | 구조체 |
키워드 | class | struct |
형식 | 참조 형식 (힙 할당) | 값 형식 (스택 할당) |
복사 | 얕은 복사 (Shallow Copy) | 깊은 복사 (Deep Copy) |
인스턴스 생성 | new 연산자와 생성자 필요 | 선언만으로도 생성 가능 (new 연산자로 생성해도 됨) |
생성자 | 매개변수 없는 생성자 선언 가능 | 매개변수 없는 생성자 선언 불가능 |
상속 | 가능 | 값 형식이므로 상속 불가능 |
1. 구조체는 값 형식이다.
알아야할 제일 큰 대표적인 차이점이 구조체는 참조 형식이 아니라 값 형식이라는 점이다.
그렇기 때문에 스택에 할당되게 되고, 선언한 블록의 끝에 도달하면 자동으로 반납처리 된다.
Unity에서 스크립트를 작성하다 보면, 다음과 같이 쓸 일이 많다.
public class Test : MonoBehaviour
{
void Update()
{
transform.position = new Vector3(1f, 2f, 3f);
}
}
여기서 Vector3는 클래스가 아니라 구조체다. new 연산자를 사용해도, 구조체이기 때문에 스택 할당이 된다.
그렇기 때문에 매 프레임마다 실행되는 Update() 함수에서 사용해도 가비지는 전혀 생성되지 않는다.
2. 값 형식이기 때문에 값이 복사되어 전달된다.
값 형식이기 때문에 복사를 하거나, 매개변수 등에 넣으면 원본이 아닌 복사본을 넘기게 된다.
이 때, 복사를 할 경우에는 깊은 복사가 일어나게 된다.
struct MyStructure
{
public int data;
// public MyStructure() { } 구조체는 매개변수 없는 생성자 생성 불가능
}
MyStructure myStruct1;
myStruct1.data = 10;
MyStructure myStruct2;
myStruct2 = myStruct1; // myStruct2.data는 10
myStruct1.data = 20; // myStruct1.data는 20,
// myStruct2.data는 10
참고로 구조체는 매개변수 없는 생성자를 선언할 수 없다. 대신, CLR이 구조체의 각 필드를 기본값으로 초기화를 해준다.
3. 변경불가능(Immutable) 객체로 만들 수 있다.
객체는 속성(상태)와 기능(행위)로 이루어진다.
상태의 변화를 허용하는 객체를 변경가능(Mutable) 객체라고 하고, 허용하지 않는 객체를 변경불가능(Immutable) 객체라고 한다.
구조체는 클래스와 달리, 변경불가능 객체를 만드는 것이 가능하다.
readonly struct 구조체이름
{
...
}
readonly를 이용하여 구조체를 선언하면, 컴파일러는 해당 구조체의 모든 필드가 readonly로 선언되도록 강제한다.
readonly struct ImmutableStruct
{
public readonly int ImmutableField;
// public int MutableField; 컴파일 오류
}
그렇다면, 변경불가능 객체는 왜 사용하는 걸까? 여러가지가 있지만, 대표적인 내용들은 다음과 같다.
- 멀티 스레드 간에 동기화(Synchronization)를 할 필요가 없기 때문에 프로그램 성능 향상이 가능하다.
- 버그로 인한 상태(데이터)의 오염을 막을 수 있다.
2번 말의 의미는, 수많은 스레드에서 객체의 상태를 변경시켜 버그를 발생시킨다면 스레드들을 전부 조사해야 하는 상황이 발생하는 걸 막을 수 있다는 말이다.
4. 읽기 전용 메소드를 선언할 수 있다.
읽기 전용 메소드 역시 구조체에서만 선언이 가능하다. 프로그래머도 사람이기 때문에 읽기만 하고 수정해선 안 되는 데이터를 수정해버리는 실수를 하기도 한다. 그걸 이제 컴파일러의 도움을 받아, 쉽게 알아차리면 좋지 않을까?
책에 좋은 예제가 있길래 한 번 같이 타이핑하며 공부 해본다.
에어컨 설정을 담는 구조체 코드를 작성한다고 해보자.
struct ACSetting
{
public double target; // 희망 온도
public double currentInCelsius; // 현재 온도(섭씨)
}
이 구조체에 섭씨로 되어 있는 현재 온도를 화씨로 변환해서 반환하는 메소드를 추가했다.
public double GetFahrenheit()
{
target = currentInCelsius * 1.8 + 32; // 화씨 계산 결과를 target에 저장
return target;
}
위에서 말했던 프로그래머의 실수가 일어날 수 있다는 부분이 이런 경우를 말한다.
화씨 온도를 계산해서 반환하기만 하면 되는데, 목표 온도를 건드려버렸다.
이런 실수를 사전에 예방하고자, 읽기 전용 메소드를 선언한다.
public readonly double GetFahrenheit()
{
// target = currentInCelsius * 1.8 + 32; 컴파일 오류
return target;
}
위와 같이, readonly 한정자를 이용해서 메소드에게 상태를 바꾸지 않도록 강제할 수 있다.
- 이 글은 <이것이 C#이다> 책을 바탕으로 공부한 글입니다.
'Languages > C#' 카테고리의 다른 글
[C#] 객체지향의 꽃, 인터페이스(Interface) - 1편 (2) | 2022.03.22 |
---|---|
[C#] 여러 필드를 담을 수 있는 구조체, 튜플(Tuple) (0) | 2022.03.21 |
[C#] 확장 메소드 (Extension Method) (0) | 2022.03.21 |
[C#] 중첩 클래스(Nested Class)와 분할 클래스(Partial Class) (2) | 2022.03.21 |
[C#] const와 readonly 둘 다 읽기 전용인데, 뭐가 다를까? (0) | 2022.03.21 |