static 은 객체가 아니라 클래스에 붙는다
처음 만든 C# 코드는 이렇게 생겼습니다.
static void Main(string[] args)
{
Console.WriteLine("안녕");
}
여기에 설명 안 하고 넘어간 것이 두 개 있습니다.
Main 앞의 static 이 무엇인지, 그리고 Console 을 new 로 만들지 않았는데 왜 WriteLine 을 부를 수 있는지입니다.
앞에서 메서드는 객체에 붙어 있고, 객체는 new 로 만든다고 했습니다. 이 두 줄은 그 규칙을 어기는 것처럼 보입니다.
static이 붙으면 객체가 아니라 클래스에 붙습니다. 객체를 안 만들어도 부를 수 있습니다.
static 은 이렇게 생겼다
필드나 메서드 앞에 단어 하나를 더 붙이는 것이 전부입니다.
class Dog
{
public static int Count; // 클래스에 하나
public int Age; // 객체마다 하나
public Dog()
{
Count = Count + 1; // 태어날 때마다 하나씩
}
}
부를 때는 이렇게 씁니다.
Dog a = new Dog();
Dog b = new Dog();
a.Age = 3; // 객체를 통해서
Console.WriteLine(Dog.Count); // 클래스를 통해서. 2
a.Count 라고 쓰면 컴파일 에러입니다. Count 는 a 의 것이 아니라 Dog 의 것이기 때문입니다.
반대로 Dog.Age 도 에러입니다. Age 는 객체가 있어야 존재합니다.
객체가 늘어나도 하나뿐이다
Dog 를 세 마리 만들어 봅니다. Age 는 세 개가 생기고 Count 는 계속 하나입니다.
Age 는 개를 만들 때마다 자리가 하나씩 더 생깁니다. Count 는 자리가 처음부터 하나고 값만 바뀝니다.
그래서 “몇 마리 만들었나” 같은 것을 담기에 맞습니다. 어느 한 마리의 것이 아니라 Dog 전체의 것이기 때문입니다.
그래서 Main 이 static 이다
이제 처음 질문으로 돌아갑니다.
프로그램이 시작되는 순간에는 아직 아무 객체도 없습니다. new 를 부른 코드가 아직 한 줄도 안 돌았으니까요.
그런데 실행을 시작하려면 메서드를 하나는 불러야 합니다.
객체가 없어도 부를 수 있는 메서드가 필요합니다. 그게 static 입니다.
Console.WriteLine 도 같은 이유입니다. 화면에 찍는 일은 어느 한 Console 객체의 사정이 아니라 프로그램 전체의 일입니다.
Console 을 굳이 만들게 하는 것이 더 이상합니다.
Console.WriteLine("안녕"); // static
Math.Max(3, 7); // static
int.Parse("123"); // static
앞에서 쓴 것들이 전부 이것이었습니다. 객체를 만든 기억이 없었던 이유입니다.
static 메서드 안에서는 this 를 쓸 수 없다
이건 규칙이라기보다 결과입니다.
class Dog
{
public int Age;
public static int Count;
public static void 출력()
{
Console.WriteLine(Count); // 됩니다
Console.WriteLine(Age); // 컴파일 에러
}
}
Dog.출력() 은 객체 없이 부를 수 있습니다. 그런데 Age 는 객체마다 다른 값입니다.
객체가 없는데 Age 를 읽으라고 하면 누구의 Age 인지 답할 방법이 없습니다.
그래서 컴파일러가 아예 못 쓰게 막습니다. 실행하다 터지는 것보다 낫습니다.
반대 방향은 됩니다. 객체가 있는 메서드에서 Count 를 읽는 것은 문제가 없습니다. Count 는 언제나 하나뿐이니까요.
언제 생기고 언제 사라지나
객체는 new 할 때 생기고, 아무도 안 가리키면 나중에 GC 가 치웁니다. 앞에서 본 그대로입니다.
static 필드는 다릅니다. 그 클래스를 처음 건드리는 순간 딱 한 번 만들어지고, 그 뒤로 프로그램이 끝날 때까지 남습니다. 치워지지 않습니다. 가리키는 사람이 없어서 치우는 것인데, static 필드는 클래스가 계속 가리키고 있기 때문입니다.
class 설정
{
public static List<string> 기록 = new List<string>();
}
여기에 무언가를 계속 넣기만 하면 그 리스트는 프로그램이 끝날 때까지 안 줄어듭니다. 넣은 객체들도 리스트가 붙잡고 있으니 같이 안 치워집니다.
편해서 위험하다
static 은 어디서든 부를 수 있습니다. 그래서 편합니다.
그런데 어디서든 부를 수 있다는 말은 어디서든 바꿀 수 있다는 말이기도 합니다.
public static int 점수;
이렇게 하나 만들어 두면 프로그램 어느 구석에서든 점수 를 고칠 수 있습니다.
값이 이상해졌을 때 어디서 그랬는지 찾을 방법이 없습니다. 객체에 넣어 뒀다면 그 객체를 들고 있는 코드만 보면 됐을 텐데요.
그래서 static 은 이런 것에 씁니다.
- 아무 상태도 안 들고 값만 계산해서 돌려주는 것 (
Math.Max) - 정말로 프로그램에 하나뿐이어야 하는 것
- 클래스 전체의 통계처럼 어느 한 객체의 것이 아닌 것
값이 굴러다니는 것을 담기 시작하면 그때부터는 전역 변수입니다.
정리
static이 붙으면 객체가 아니라 클래스에 붙습니다.Dog.Count처럼 부릅니다- 객체를 몇 개 만들든 static 필드는 하나입니다
- 프로그램이 시작될 때는 객체가 없으므로
Main은 static 이어야 합니다 - static 메서드에서 인스턴스 필드를 못 쓰는 것은 누구의 것인지 답할 수 없어서입니다
- 클래스를 처음 건드릴 때 한 번 만들어지고 프로그램이 끝날 때까지 남습니다. 치워지지 않습니다