본문 바로가기
C#

구조체를 List<T> 에 추가하는 방법들

by Oz Driver 2025. 7. 19.

동일한 구조체를 여러 개 나열할 경우, 구조체 배열을 사용하는 방법도 있겠지만, 여기서는 List<T>를 사용하는 방법에 대해 다뤄보겠습니다. List<T>에 값을 추가할 때는 크게 두 가지 방식이 있습니다.

1. Add() 함수를 이용해 데이터를 하나씩 추가하는 방식

2. 선언과 동시에 데이터를 중괄호 { } 안에 나열하는 방식

그 외에 LINQ를 이용한 방식도 있지만, 여기서는 다루지 않겠습니다.
(솔직히 제가 잘 쓰지를 않아서 모릅니다. 😅)

어떤 방식이 더 좋다기보다는, 상황에 맞게 또는 선호하는 코드 스타일에 따라 선택하면 됩니다.

 

대표적인 초기화 방식

1. Add() 함수 이용

// new 로 List<T> 를 생성
List<T> list = new List<T>();

// 생성된 list 에 Add() 로 값을 추가.
T value = new T();
list.Add(value);

 

•   이 방식은 직관적입니다. 

•   List<T> 를 생성한 후에, 구조체 T 를 생성하고 값을 채워넣은 뒤 Add() 를 이용해 하나씩 추가합니다.

 

2. 중괄호 { } 이용

List<T> list = new List<T>
{
    new T { ... },
    new T { ... }
};

또는

List<T> list = new List<T>()
{
    new T { ... },
    new T { ... }
};

 

•   new 선언 후에 () 는 생략 가능합니다. ( C# 3.0 부터 지원 )

•   이 방식은 선언과 동시에 중괄호 { }  안에서 구조체 T 를 생성하고 값을 채워넣습니다.

•   중괄호 { } 안에서 값을 나열하면 컴파일러가 내부적으로 Add() 를 자동 호출합니다.

 

두 방식의 미묘한 차이점

언뜻 보면 두 방식이 비슷해 보이지만,

•   1) 방식은 new (); 로 객체를 생성한 뒤, Add() 함수를 통해 데이터를 추가합니다. 

•   2) 방식은 new 선언 후, 괄호 () 의 유무와 상관없이 기본 생성자가 호출된다는 점은 동일합니다.

  -   다만, 곧바로 중괄호 { } 를 사용해 코드를 이어갑니다. 

  -   이때, 중괄호 { } 는 단순한 코드 블록이 아니라, 컬렉션에 값을 추가할 때, Add() 함수를 자동으로 호출하는 특별한 문법입니다.

 

구체적인 예시

이 방법들에 대한 조금 더 구체적인 예시를 들어보겠습니다.

1. Add() 함수 이용

struct Quest
{
    public string name;
    public int gold;
    public int exp;
}

List<Quest> questList = new List<Quest>();

Quest quest = new Quest();
quest.name = "Collect 5 Herbs";
quest.gold = 50;
quest.exp = 20;
questList.Add(quest);

// Quest quest2 = new Quest() 처럼
// new 로 새로운 변수를 생성하지 않고 재활용함.
quest.name = "Kill 10 Goblins";
quest.gold = 100;
quest.exp = 50;
questList.Add(quest);

// Add() 함수는 내부적으로 힙에 값을 복사하기 때문에
// 원본 quest 에 값을 덮어써도 상관이 없음.
quest.name = "Defeat the Dragon";
quest.gold = 500;
quest.exp = 300;
questList.Add(quest);

 

•   구조체가 복잡한 경우, Add() 함수로 데이타를 하나씩 추가해 나갑니다. 

•   Add() 함수는 스택에 저장되어 있는 값을 추가할 때는, 값 복사가 일어납니다.

•   따라서 Quest 변수 quest 를 하나만 생성해서 재활용해도 이미 Add() 를 통해 값복사가 일어났기 때문에 이후에 quest 변수를 재활용하여 데이타를 다른 값으로 덮어써도 상관이 없습니다. 

 

2. 중괄호 { } 이용

List<Quest> questList = new List<Quest>
{
    new Quest { Name = "Kill 10 Goblins", Gold = 100, Exp = 50 },
    new Quest { Name = "Collect 5 Herbs", Gold = 50, Exp = 20 },
    new Quest { Name = "Defeat the Dragon", Gold = 500, Exp = 300 }
};

 

•  구조체가 간단하다면, 한줄로 표현이 가능하기 때문에 코드가 간결해 집니다.

 

3. 함수를 이용한 방식

Quest CreateQuest( string name, int gold, int exp )
{
    // (1) 스택/레지스터에 구조체 생성
    Quest q = new Quest(); 
    
    q.Name = name;
    q.Gold = gold;
    q.Exp = exp;
    
    // (2) 호출자에게 값 복사
    return q;              
}

// (3) Add 시 힙에 복사
questList.Add( CreateQuest("Defeat the Dragon", 500, 300) );

 

•   반복적인 Add() 를 피하기 위해 이 부분을 함수로 작성했습니다. 

•   이 때, Quest 구조체는 총 2번 값 복사발생합니다. 

  1)   return q; 를 반환할 때 값이 복사됩니다. 이 때 함수 내부에서 선언한 q 는 중괄호를 벗어나면 사라집니다.

  2)   questList.Add() 함수 내부에서는 위에서 설명한 바와 같이, 반환값 q 가 힙으로 또 한번 복사됩니다. 

•   구조체 크기가 크지 않다면 이 정도 부하는 무시할 정도의 수준입니다.

 

Add()의 내부 동작

구조체인 경우 (값 타입)

Quest quest = new Quest { ... }; 

// 힙에 복사됨
questList.Add(quest);

 

•   계속 언급한 것처럼, 구조체는 Add() 함수 내부에서는 힙으로 값 복사가 일어납니다. 

•   그렇기 때문에, List<T> 에 Add() 된 이후에는 원본 데이타가 덮어써지거나 사라져도, 아무 문제가 발생하지 않습니다.

 

클래스인 경우 (참조 타입)

QuestClass quest = new QuestClass();

// 참조값(주소)만 복사됨
questList.Add(quest);

 

•   만약 Quest 가 클래스라고 하면 Add() 함수는 값 복사 없이 주소만 복사됩니다. 

•   그렇기 때문에 원본 데이타를 수정하면, 내부 리스트로 같이 변경됩니다. 

 

마무리

   List<T> 에 데이타를 추가하는 방식은 대표적인 2 가지 방식이 있습니다.

  1)   new() 로 List<T> 를 생성한 후, Add() 함수로 데이타를 추가하는 방식

  2)   new 이후에 곧바로 중괄호 { } 를 써서 데이타를 추가하는 방식

  Add() 함수는 구조체일 경우 값 복사, 클래스일 경우는 참조 복사됩니다.