---
title: "생성자는 태어날 때 딱 한 번 실행된다"
description: "만들고 나서 값을 채우면 빠뜨릴 수 있습니다. 만드는 순간 채우게 하면 빠뜨릴 방법이 없습니다."
author: "유니얼"
pubDate: 2026-09-09
category: "programming/csharp/basics"
tags: ["C#", "기초"]
canonical: https://blog.unialgames.com/posts/csharp-constructor/
---
앞 글까지의 코드는 이렇게 만들었습니다.

```csharp
Dog a = new Dog();
a.Name = "바둑";
a.Age = 3;
```

세 줄이 한 벌입니다. 그런데 문법이 그걸 모릅니다.

```csharp
Dog b = new Dog();
b.Name = "초코";
// Age 를 빠뜨렸습니다
```

컴파일도 되고 실행도 됩니다. `b.Age` 는 `0` 입니다.
0살인 개인지 안 넣은 것인지 구분할 방법이 없습니다.

> **만드는 순간 값을 받게 하면 빠뜨릴 방법이 없어집니다.**

## 생성자는 이렇게 생겼다

```csharp
class Dog
{
    public string Name;
    public int Age;

    public Dog(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Dog a = new Dog("바둑", 3);
```

메서드와 비슷한데 두 가지가 다릅니다.

- **이름이 클래스와 같습니다.**
- **반환 타입이 없습니다.** `void` 도 안 적습니다.

돌려줄 것을 적지 않는 이유는 하는 일이 정해져 있기 때문입니다.
객체를 만드는 것이지 값을 계산하는 것이 아닙니다.

`new Dog("바둑", 3)` 의 괄호에 넣은 값이 여기로 들어옵니다.
지금까지 `new Dog()` 라고 빈 괄호를 썼는데, 그 괄호가 이 자리였습니다.

이제 `Age` 를 빠뜨릴 수 없습니다. 안 넣으면 컴파일 에러입니다.

## 매개변수 이름과 필드 이름

위 코드에서 매개변수는 `name`, 필드는 `Name` 입니다.
C# 에서는 필드를 대문자로, 매개변수를 소문자로 시작하는 관습이 있어서 이렇게 겹치지 않습니다.

같게 쓰고 싶으면 `this` 를 붙입니다.

```csharp
public Dog(string Name, int Age)
{
    this.Name = Name;    // this.Name 은 필드, Name 은 매개변수
    this.Age = Age;
}
```

`this` 는 **지금 이 객체** 를 가리킵니다.
`a.Bark()` 로 부르면 그 안에서 `this` 는 `a` 입니다.

메서드 안에서 필드를 그냥 쓸 때도 사실은 `this` 가 생략된 것입니다.
겹치는 이름이 없으면 안 써도 됩니다.

## 만들면 반드시 실행된다

생성자는 **객체가 태어나는 순간 딱 한 번** 실행됩니다.
부르는 것을 잊을 수가 없습니다. `new` 가 곧 호출이니까요.

그래서 "이 객체는 항상 이런 상태로 시작한다" 를 보장할 수 있습니다.

```csharp
class Player
{
    public string Name;
    public int Hp;

    public Player(string name)
    {
        Name = name;
        Hp = 100;        // 넘겨받지 않고 정해 둡니다
    }
}
```

`Hp` 는 밖에서 받지 않습니다. 모든 플레이어는 100 으로 시작합니다.
이 규칙이 코드 한 곳에 적혀 있고, 새로 만드는 모든 객체가 그 규칙을 따릅니다.

## 하나라도 만들면 기본 생성자가 사라진다

지금까지 `new Dog()` 를 빈 괄호로 썼습니다. 생성자를 안 만들었는데도 됐습니다.
아무것도 안 만들면 컴파일러가 **매개변수 없는 생성자**를 자동으로 만들어 주기 때문입니다.

그런데 하나라도 직접 만들면 그게 사라집니다.

```csharp
class Dog
{
    public Dog(string name, int age) { ... }
}

Dog a = new Dog();      // 컴파일 에러
```

일부러 그렇게 되어 있습니다. "이름과 나이를 받아야 한다" 고 적어 뒀는데
빈 괄호로 만들 수 있으면 그 규칙이 반쪽이 됩니다.

둘 다 열어 두고 싶으면 생성자를 여러 벌 만들면 됩니다.
앞에서 본 오버로딩이 생성자에도 그대로 적용됩니다.

```csharp
class Dog
{
    public string Name;
    public int Age;

    public Dog(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public Dog(string name) : this(name, 0)
    {
    }
}
```

`: this(name, 0)` 은 **같은 클래스의 다른 생성자를 먼저 부른다**는 뜻입니다.
같은 코드를 두 번 적지 않으려고 씁니다.

기본값 매개변수로도 됩니다. 이쪽이 짧습니다.

```csharp
public Dog(string name, int age = 0)
```

## static 은 틀에 붙는다

필드는 객체마다 하나씩 생긴다고 했습니다. `static` 을 붙이면 달라집니다.

```csharp
class Dog
{
    public static int Count = 0;     // 클래스 전체에 하나
    public string Name;              // 객체마다 하나

    public Dog(string name)
    {
        Name = name;
        Count++;
    }
}

new Dog("바둑");
new Dog("초코");

Console.WriteLine(Dog.Count);     // 2
```

`Count` 는 붕어빵이 아니라 **틀에 붙어 있습니다.** 개가 몇 마리든 하나뿐입니다.
그래서 객체를 만들지 않고 `Dog.Count` 로 씁니다.

<figure class="diagram">
<svg viewBox="0 0 680 190" role="img" aria-label="static 필드는 클래스에 하나, 일반 필드는 객체마다 하나씩 있는 그림">
<g fill="var(--bg)" stroke="var(--border)">
<rect x="24" y="40" width="180" height="100" rx="8"></rect>
<rect x="300" y="30" width="150" height="56" rx="8"></rect>
<rect x="300" y="102" width="150" height="56" rx="8"></rect>
</g>
<text x="40" y="66" font-size="13" fill="var(--text-muted)">클래스 Dog</text>
<rect x="40" y="82" width="148" height="38" rx="6" fill="var(--accent-soft)" stroke="var(--accent)"></rect>
<text x="52" y="106" font-size="13" font-family="var(--font-mono)" fill="var(--accent)">static Count: 2</text>
<g font-size="13" font-family="var(--font-mono)" fill="var(--text)">
<text x="316" y="54">Name: "바둑"</text>
<text x="316" y="126">Name: "초코"</text>
</g>
<g font-size="12" fill="var(--text-muted)">
<text x="316" y="74">객체 하나</text>
<text x="316" y="146">객체 둘</text>
</g>
<g stroke="var(--border)" stroke-width="1.3" fill="none">
<path d="M204 78 L300 58"></path>
<path d="M204 102 L300 130"></path>
</g>
<text x="470" y="94" font-size="13" fill="var(--text-muted)">둘이 같은 Count 를 본다</text>
</svg>
<figcaption>static 은 틀에 붙고, 나머지는 찍어낸 것마다 붙는다.</figcaption>
</figure>

`static` 메서드도 같습니다. 객체 없이 부릅니다.

```csharp
Math.Sqrt(16);           // Math 객체를 만든 적이 없습니다
int.TryParse(s, out n);  // int 에 붙은 static 메서드입니다
Console.WriteLine();     // 이것도 마찬가지입니다
```

지금까지 마침표 앞에 **클래스 이름**이 오던 것들이 전부 `static` 이었습니다.
객체 이름이 오면 그 객체의 것이고, 클래스 이름이 오면 틀의 것입니다.

한 가지 규칙이 있습니다. **`static` 메서드 안에서는 일반 필드를 못 씁니다.**

```csharp
public static void Print()
{
    Console.WriteLine(Name);    // 컴파일 에러
}
```

`Name` 은 객체마다 하나인데, `static` 메서드는 객체 없이 불립니다.
**누구의 `Name`** 인지 정할 수가 없습니다.

## 정리

- 생성자는 객체가 태어나는 순간 딱 한 번 실행됩니다.
- 이름이 클래스와 같고 반환 타입을 안 적습니다. `new` 의 괄호에 넣은 값이 여기로 옵니다.
- 만들면서 값을 받게 하면 빠뜨릴 방법이 없어집니다.
- `this` 는 지금 이 객체입니다. 이름이 겹칠 때 필드와 매개변수를 구분합니다.
- 생성자를 하나라도 직접 만들면 매개변수 없는 기본 생성자가 사라집니다.
- 생성자도 오버로딩됩니다. `: this(...)` 로 다른 생성자를 먼저 부를 수 있습니다.
- `static` 은 클래스 전체에 하나입니다. 객체 없이 클래스 이름으로 씁니다.
- `static` 메서드 안에서는 객체마다 있는 필드를 쓸 수 없습니다.