로그인게임하기
Unity C# 강좌

[Unity C# 8] var, 초기화 문법, 프로퍼티로 짧고 안전하게 선언하기

2026.10.14

Unity C# 강좌 · 8편

타입 이름을 두 번 쓰는 선언, 만들고 나서 한 줄씩 채우는 목록, 아무나 고칠 수 있는 필드. 이번 편에서는 var 와 초기화 문법으로 선언을 짧게 만들고, 프로퍼티로 값을 읽고 쓰는 규칙을 클래스 안에 가둡니다.

수업 정보

2024년 부산 콘텐츠 아카데미 퍼스널 트레이닝 수업에서 MyLesson Unity 실습 프로젝트의 Strategy_08_Var.cs · Strategy_13_ClassInitializer.cs · Strategy_12_Property.cs 로 가르친 내용입니다. 수업은 다섯 단계로 나뉘고, 단계마다 코드가 조금씩 바뀝니다.

VAR STEP 1

var, 타입은 오른쪽을 보고 정한다

Strategy_08_Var.cs수업 자료 1단계31–51줄
public void Run()
{
    var i = 1;
    var j = "j";
    Program.WriteLine("i={0}, j={1}", i, j);

    //Dictionary<long, List<int>> userInventory = new Dictionary<long, List<int>>();
    var userInventory = new Dictionary<long, List<int>>();

    var k = new GameObjectCreator();
    var createdObj = k.Create<Image>("K Instance Object");
    Program.WriteLine(createdObj);

    createdObj.GetComponent<Image>().color = Color.blue;

    // Error case 1
    //var l;
    //l = 5;

    // Error case 2
    //createdObj = 500;

var 는 "타입을 오른쪽 값에서 정해 달라" 는 표시입니다. var i = 1 이면 int, var j = "j" 면 string 입니다. Dictionary<long, List<int>> 처럼 긴 타입을 두 번 쓰지 않아도 됩니다.

실행 결과게임 화면의 출력 창
i=1, j=j
K Instance Object (UnityEngine.GameObject)

var 로 되는 것

  • var i = 1; → int
  • var k = new GameObjectCreator();
  • var obj = k.Create<Image>(…); → GameObject

var 로 안 되는 것 (주석의 Error case)

  • var l; 처럼 값 없이 선언하기 (타입을 알 수 없음)
  • createdObj = 500; 처럼 나중에 다른 타입 넣기
  • var 는 동적 타입이 아니다. 컴파일할 때 타입이 정해진다
GameObjectCreator.Create<T>

2편의 제네릭이 다시 나옵니다. where T : MonoBehaviour 조건으로, 빈 GameObject 를 만들고 T 컴포넌트를 붙여 돌려줍니다. Image 를 넘기면 화면에 파란 사각형이 생깁니다.

VAR STEP 2 · 3

배열 · 리스트 · 사전 초기화

Strategy_08_Var.cs수업 자료 2단계28–40줄
int[] array1_1 = new int[4] { 0, 1, 2, 3 };
int[] array1_2 = { 0, 1, 2, 3 }; // 암시적 축약
Program.WriteList(array1_2);

float[] array2_1 = new float[3] { 0.1f, 0.2f, 0.3f };
float[] array2_2 = new float[] { 0.1f, 0.2f, 0.3f }; // 암시적 축약
var array2_3 = new float[] { 0.1f, 0.2f, 0.3f }; // var 사용
Program.WriteList(array2_3);

string[,] array3_1 = new string[2, 3] { { "Heloon", "Leon", "Lucia" }, { "Darkan", "Karsian", null } };
string[,] array3_2 = new string[,] { { "Heloon", "Leon", "Lucia" }, { "Darkan", "Karsian", null } }; // 암시적 축약
var array3_3 = new string[,] { { "Heloon", "Leon", "Lucia" }, { "Darkan", "Karsian", null } }; // var 사용
Program.WriteList(array3_3);

배열은 중괄호 안에 값을 늘어놓아 만들 수 있고, 크기와 타입은 값에서 알아냅니다. 2차원 배열 string[,] 도 같은 방식입니다.

Strategy_08_Var.cs수업 자료 3단계36–53줄
List<string> myItems_1 = new List<string>();
List<string> myItems_2 = new List<string> { "ThunderBow", "ThunderSword", "ThunderShield", "FireArrow" }; // 원소 초기화
var myItems_3 = new List<string>() { "ThunderBow", "ThunderSword", "ThunderShield", "FireArrow" }; // var 사용

Program.WriteList(myItems_3);

Dictionary<string, int> weaponAttackPoints_1 = new Dictionary<string, int>();
string secondSword = "SecondSword";
int secondSwordAttackPoint = 20;
WeaponInfo thirdSword = new WeaponInfo { Name = "ThirdSword", AttackPoint = 30 };
var weaponAttackPoints_2 = new Dictionary<string, int> // 원소 초기화 및 var 사용
{
    { "FirstSword", 10 },
    { secondSword, secondSwordAttackPoint },
    { thirdSword.Name, thirdSword.AttackPoint }
};

Program.WriteList(weaponAttackPoints_2);

List 와 Dictionary 도 만들면서 바로 채울 수 있습니다(컬렉션 초기화). 사전은 { 키, 값 } 묶음을 늘어놓습니다. 구조체 WeaponInfo 는 new WeaponInfo { Name = …, AttackPoint = … } 처럼 필드 이름을 적어 만들었습니다(객체 초기화).

3단계 실행 결과게임 화면의 출력 창
ThunderBow
ThunderSword
ThunderShield
FireArrow
[FirstSword, 10]
[SecondSword, 20]
[ThirdSword, 30]
모양뜻
new int[] { 0, 1, 2, 3 }배열 초기화. 크기는 값의 개수
new List<string> { "A", "B" }컬렉션 초기화. 만든 뒤 Add 를 차례로 부른 것과 같다
new Dictionary<string, int> { { "A", 1 } }사전 초기화. Add(키, 값) 을 부른 것과 같다
new WeaponInfo { Name = "A" }객체 초기화. 만든 뒤 속성에 값을 넣은 것과 같다
INITIALIZER

객체 안의 목록까지 한 번에

Strategy_13_ClassInitializer.cs수업 자료 최종23–31줄
public class Product
{
    public string ProductId { get; set; }

    private readonly List<SellItem> items = new List<SellItem>();
    public List<SellItem> Items
    {
        get { return items; }
    }

Product.Items 는 get 만 있는 프로퍼티입니다. 다른 리스트로 바꿔 끼울 수는 없습니다. 그런데도 아래 코드는 동작합니다.

Strategy_13_ClassInitializer.cs수업 자료 최종58–84줄
var order = new Shop
{
    Products =
    {
        new Product
        {
            ProductId = "Package.Ruby.10000.FirstBonus.1000",
            Items =
            {
                new SellItem { ItemId = "ruby", Quantity = 10000 },
                new SellItem { ItemId = "free.ruby", Quantity = 1000 }
            }
        },

        new Product
        {
            ProductId = "Package.Weapon.Archer",
            Items =
            {
                new SellItem { ItemId = "bow.legendary", Quantity = 1 },
                new SellItem { ItemId = "arrow.legendary", Quantity = 1000 }
            }
        }
    }
};

Program.WriteLine(order);

Items = { … } 는 Items 에 새 리스트를 넣는 것이 아니라, 이미 있는 Items 리스트에 Add 를 부르는 문법입니다. 그래서 setter 가 없어도 됩니다. 상점 · 상품 · 판매 품목처럼 겹겹이 들어 있는 데이터를 데이터 모양 그대로 적을 수 있습니다.

실행 결과게임 화면의 출력 창
Package.Ruby.10000.FirstBonus.1000
ruby/10000
free.ruby/1000

Package.Weapon.Archer
bow.legendary/1
arrow.legendary/1000
PROPERTY

프로퍼티, 읽고 쓰는 규칙을 클래스 안에

Monster.cs수업 자료 최종1–56줄
public class Monster
{
    private static int increment = 0;
    private static int GetNextID()
    {
        increment++;
        return increment;
    }

    public int Id { get; }

    private string name;
    public string Name { get => name; }

    private int attackPoint;
    public int AttackPoint
    {
        get
        {
            return attackPoint + buffAttackPoint;
        }
    }

    private int healthPoint;
    public int HealthPoint
    {
        get => healthPoint;
        set
        {
            Program.WriteLine("---------- {0}'s new HP: {1}", name, value);

            if (value > originHealthPoint)
                value = originHealthPoint;

            healthPoint = value;
        }
    }

    private int originHealthPoint;
    private int buffAttackPoint;

    public Monster(string name, int attackPoint, int healthPoint)
    {
        this.Id = Monster.GetNextID();
        this.name = name;
        this.attackPoint = attackPoint;
        this.healthPoint = healthPoint;
        this.originHealthPoint = healthPoint;
    }

    public void AddBuffAttackPoint(int buff)
    {
        buffAttackPoint += buff;
        Program.WriteLine("---------- Buff Attack +{0} to {1}", buff, name);
    }
}
멤버모양하는 일
Id{ get; }읽기 전용 자동 프로퍼티. 생성자에서만 값을 넣을 수 있다
Name{ get => name; }필드 name 을 읽기만 하게 내보낸다
AttackPointget 안에서 계산기본 공격력 + 버프를 더해 돌려준다. 저장된 값이 아니라 계산된 값
HealthPointget + setset 에서 최대 체력을 넘지 않게 자른다
Monster.cs수업 자료 최종68–83줄
public void Run()
{
    Monster mon1 = new Monster("Goblin", 2, 2);
    Monster mon2 = new Monster("Orc", 3, 1);

    PrintMonster(mon1);
    Program.WriteLine();
    PrintMonster(mon2);

    mon1.AddBuffAttackPoint(2);
    mon2.HealthPoint = 10;

    PrintMonster(mon1);
    Program.WriteLine();
    PrintMonster(mon2);
}
실행 결과게임 화면의 출력 창
ID: 1
Name: Goblin
AP: 2
HP: 2

ID: 2
Name: Orc
AP: 3
HP: 1
---------- Buff Attack +2 to Goblin
---------- Orc's new HP: 10
ID: 1
Name: Goblin
AP: 4
HP: 2

ID: 2
Name: Orc
AP: 3
HP: 1

밖에서는 mon2.HealthPoint = 10 처럼 필드에 값을 넣듯 씁니다. 하지만 실제로는 set 이 불려 규칙이 적용되고, 오크의 체력은 최대치 1 로 잘립니다. 필드를 public 으로 열어 두면 이런 규칙을 지킬 곳이 없습니다.

로그 순서의 함정

set 의 로그는 값을 자르기 전에 찍힙니다. 그래서 "new HP: 10" 이라고 찍히지만 실제로 저장된 값은 1 입니다. 로그는 최종 값을 정한 뒤에 찍는 습관이 좋습니다.

RETRO

돌아보며

이번 편에서 얻은 것

  • var 는 컴파일할 때 타입이 정해지는 줄임말이다
  • 컬렉션 · 객체 초기화로 데이터 모양 그대로 적을 수 있다
  • 프로퍼티로 읽기 전용, 계산 값, 값 검사를 클래스 안에 둔다

아쉬운 점

  • VAR 2단계의 2차원 배열은 마지막 칸이 null 이라, WriteList 가 null.ToString() 에서 예외를 낸다
  • HealthPoint 의 로그가 값을 자르기 전에 찍힌다

정리

  1. var 는 오른쪽을 보고 타입을 정합니다. 값 없이 선언할 수 없고, 나중에 다른 타입을 넣을 수도 없습니다.
  2. 초기화 문법은 Add 와 속성 대입의 줄임말입니다. get 만 있는 목록 프로퍼티에도 { } 로 원소를 채울 수 있습니다.
  3. 프로퍼티는 규칙을 지키는 문입니다. 밖에서는 필드처럼 쓰고, 안에서는 계산과 검사를 합니다.

다음 편에서는 한 클래스를 여러 파일로 나누는 partial, 같은 이름의 타입을 구분하는 namespace, 그리고 에디터 메뉴를 만드는 MenuItem 을 봅니다.

← 개발 노트 목록