C# 구조체

C# 카테고리를 만들어서 별도로 해야 하지만 그냥 유니티 카테고리에 올리겠습니다.
AI가 저보다 설명도 잘하고 글도 잘적기 때문에 챗 GPT 설명을 가져오겠습니다.

챗 GPT에게 한 질문입니다.
구조체는 new 했으면  어디서 삭제해?

결론부터 딱 말하면

👉 struct는 삭제 안 한다.
👉 new 했다고 해서 “해제” 개념이 없다.

왜 그런지 차근차근 보자.


struct는 힙이 아니다 (대부분)

PlayerDeadEvent evt = new PlayerDeadEvent(1);

이 코드에서 벌어지는 일 👇

struct의 특징

  • 값 타입(Value Type)

  • 보통 스택에 생성

  • 함수 끝나면 자동으로 사라짐

void Die()
{
PlayerDeadEvent evt =
new PlayerDeadEvent(1);
EventBus.Publish(evt);
}
// ← 여기서 evt는 그냥 사라짐

👉 GC 관여 없음

👉 삭제 코드 필요 없음


“new”라는 단어 때문에 헷갈리는 포인트

C#에서 new는 두 가지 의미가 섞여 있음:

class : 힙에 할당
struct : 값 초기화

👉 struct의 new
**“메모리 할당”이 아니라 “값을 채운다”**에 가까움

그럼 struct는 항상 스택이냐?

❌ 항상은 아님.
어디에 담기느냐가 중요함.

예외 1: class 필드에 들어갈 때

class Wrapper
{
public PlayerDeadEvent evt;
}

👉 이 경우 struct는 Wrapper 객체의 일부
👉 Wrapper가 GC 되면 같이 사라짐


예외 2: interface / object로 캐스팅

IEvent e = evt; // boxing

👉 boxing 발생
👉 힙에 복사됨
👉 GC 대상 ⭕

⚠️ EventBus에서 이런 짓 하면 성능 망함

👎 이렇게 하지 마라

  • 큰 데이터

  • List / class 포함 (List는 값 복사가 아니라 참조 문제 발생)

  • mutable struct

  • interface로 다루기

한 문장으로 정리

struct는 만들고 잊어도 된다.
함수 끝나면 자연 소멸한다.


결론: 배열이나 컬렉션은 struct 이벤트가 아니라 class 이벤트로 넘기는 게 안전

예시

public class DamageEvent
{
public readonly int[] Damages;

public DamageEvent(int[] damages)
{
Damages = (
int[])damages.Clone(); // 불변성 보장
}
}
EventBus.Publish(new DamageEvent(new int[] { 10, 20, 30 }));
  • 리스너들은 자유롭게 읽기 가능

  • 수정은 각자 독립적인 이벤트에서만 가능

  • GC는 배열 하나만 생성


값 변경 상관없으면 이렇게 가능

public class DamageEvent
{
public readonly int[] Damages;

public DamageEvent(int[] damages)
{
Damages = damages;
// 참조 전달
}
}
  • 값 변경해도 괜찮을 때 사용 가능

  • ⚠️ 다른 쪽에서 배열을 수정해도 문제가 없음

  • 🔹 메모리/GC 효율은 좋음 (복사 안 함)

IReadOnlyList<int> 사용 (추천)

public class DamageEvent
{
public readonly IReadOnlyList<int> Damages;

public DamageEvent(int[] damages)
{
Damages = Array.AsReadOnly(damages);
}
}
  • 배열을 읽기 전용으로 감싸서

    • Damages[0] = 10 같은 코드는 컴파일에서 막힘

  • 참조는 여전히 readonly

  • 내부 배열 수정 가능성도 막아줌


ReadOnlyMemory<int> / Span<int> (고성능)

  • Unity DOTS / Jobs 환경에서 주로 사용 (MonoBehaviour 환경에서는 고성능과 상관없음)

  • 메모리 안전 + 불변 보장

public class DamageEvent
{
public readonly ReadOnlyMemory<int> Damages;

public DamageEvent(int[] damages)
{
Damages =
new ReadOnlyMemory<int>(damages);
}
}
  • 내부 값 변경 불가

  • GC 부담 없음

  • 다만 Unity 일반 MonoBehaviour 환경에서는 조금 번거로움

즉, ReadOnlyMemory는 DOTS 환경에서 진짜 고성능이 되고,
MonoBehaviour 환경에서는 주로 안전성 + 불변 보장 용도로 유용