static 은 객체가 아니라 클래스에 붙는다
Esc

    static 은 객체가 아니라 클래스에 붙는다

    –C#

    처음 만든 C# 코드는 이렇게 생겼습니다.

    static void Main(string[] args)
    {
        Console.WriteLine("안녕");
    }

    여기에 설명 안 하고 넘어간 것이 두 개 있습니다.

    Main 앞의 static 이 무엇인지, 그리고 Console 을 new 로 만들지 않았는데 왜 WriteLine 을 부를 수 있는지입니다. 앞에서 메서드는 객체에 붙어 있고, 객체는 new 로 만든다고 했습니다. 이 두 줄은 그 규칙을 어기는 것처럼 보입니다.

    static 이 붙으면 객체가 아니라 클래스에 붙습니다. 객체를 안 만들어도 부를 수 있습니다.

    static 은 이렇게 생겼다

    필드나 메서드 앞에 단어 하나를 더 붙이는 것이 전부입니다.

    public static int Count;public int Age;클래스에 하나. 객체가 없어도 있습니다객체마다 하나. 객체가 있어야 있습니다부르는 법도 다릅니다. Dog.Count 와 a.Age 입니다.
    붙는 자리가 다르면 부르는 법도 다릅니다.
    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 메서드에서 인스턴스 필드를 못 쓰는 것은 누구의 것인지 답할 수 없어서입니다
    • 클래스를 처음 건드릴 때 한 번 만들어지고 프로그램이 끝날 때까지 남습니다. 치워지지 않습니다