동일한 구조체를 여러 개 나열할 경우, 구조체 배열을 사용하는 방법도 있겠지만, 여기서는 List<T>를 사용하는 방법에 대해 다뤄보겠습니다. List<T>에 값을 추가할 때는 크게 두 가지 방식이 있습니다.
1. Add() 함수를 이용해 데이터를 하나씩 추가하는 방식
2. 선언과 동시에 데이터를 중괄호 { } 안에 나열하는 방식
그 외에 LINQ를 이용한 방식도 있지만, 여기서는 다루지 않겠습니다.
(솔직히 제가 잘 쓰지를 않아서 모릅니다. 😅)
어떤 방식이 더 좋다기보다는, 상황에 맞게 또는 선호하는 코드 스타일에 따라 선택하면 됩니다.
대표적인 초기화 방식
1. List<T> 를 new (); 로 생성한 후에, 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> 를 new 로 생성과 동시에 값을 추가
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. List<T> 를 new (); 로 생성한 후에, 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<T> 를 new 로 생성과 동시에 값을 추가
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. 생성용 함수를 이용한 Add() 방식
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() 함수는 구조체일 경우 값 복사, 클래스일 경우는 참조 복사됩니다.
'C#' 카테고리의 다른 글
foreach() 가 내부적으로 동작하는 방식 (0) | 2025.07.15 |
---|---|
Sort() 를 보다 깊이있게 이해하기 (0) | 2025.07.10 |
배열과 리스트를 정렬하는 방법 (0) | 2025.07.10 |
람다를 이용해 버블 정렬 구현하기 (0) | 2025.07.10 |
C#에서 튜플(Tuple)이란? (1) | 2025.07.09 |