정적 생성자(C# 프로그래밍 가이드)

정적 생성자는 정적 데이터를 초기화하거나 한 번만 수행해야 하는 특정 작업을 수행하는 데 사용됩니다. 첫 번째 인스턴스가 만들어지거나 정적 멤버가 참조되기 전에 자동으로 호출됩니다. 정적 생성자는 최대 한 번 호출됩니다.

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    static SimpleClass()
    {
        baseline = DateTime.Now.Ticks;
    }
}

정적 초기화의 일부인 몇 가지 작업이 있습니다. 해당 작업은 다음 순서로 수행됩니다.

  1. 정적 필드는 0으로 설정됩니다. 이는 일반적으로 런타임에 의해 수행됩니다.
  2. 정적 필드 이니셜라이저가 실행됩니다. 가장 많이 파생된 형식의 정적 필드 이니셜라이저가 실행됩니다.
  3. 기본 형식 정적 필드 이니셜라이저가 실행됩니다. 직접 기본으로 시작하여 각 기본 형식을 거쳐 System.Object까지의 정적 필드 이니셜라이저입니다.
  4. 기본 정적 생성자가 실행됩니다. Object.Object로 시작하여 각 기본 클래스를 거쳐 직접 기본 클래스까지 모든 정적 생성자.
  5. 정적 생성자가 실행됩니다. 해당 형식에 대한 정적 생성자가 실행됩니다.

모듈 이니셜라이저은 정적 생성자의 대안이 될 수 있습니다. 자세한 내용은 모듈 이니셜라이저 사양을 참조하세요.

설명

정적 생성자에는 다음과 같은 속성이 있습니다.

  • 정적 생성자는 액세스 한정자를 사용하거나 매개 변수를 갖지 않습니다.
  • 클래스 또는 구조체에는 한 개의 정적 생성자만 사용할 수 있습니다.
  • 정적 생성자는 상속하거나 오버로드할 수 없습니다.
  • 정적 생성자는 직접 호출할 수 없으며, CLR(공용 언어 런타임)을 통해서만 호출할 수 있습니다. 자동으로 호출됩니다.
  • 사용자는 프로그램에서 정적 생성자가 실행되는 시기를 제어할 수 없습니다.
  • 정적 생성자는 자동으로 호출되며 첫 번째 인스턴스가 생성되거나 (기본 클래스가 아닌) 해당 클래스에서 선언된 정적 멤버가 참조되기 전에 클래스를 초기화합니다. 정적 생성자는 인스턴스 생성자보다 먼저 실행됩니다. 정적 필드 변수 이니셜라이저가 정적 생성자의 클래스에 있는 경우 클래스 선언에 나타나는 텍스트 순서대로 실행됩니다. 이니셜라이저는 정적 생성자가 실행되기 직전에 실행됩니다.
  • 정적 필드를 초기화하는 정적 생성자를 제공하지 않으면 모든 정적 필드가 C# 형식의 기본값에 나열된 기본값으로 초기화됩니다.
  • 정적 생성자가 예외를 throw하는 경우 런타임에서 생성자를 다시 호출하지 않으며 애플리케이션 도메인의 수명 동안 형식이 초기화되지 않은 상태로 유지됩니다. 가장 일반적으로, 정적 생성자가 형식을 인스턴스화할 수 없는 경우 또는 정적 생성자 내에서 발생하는 처리되지 않은 예외에 대해 TypeInitializationException 예외가 throw됩니다. 소스 코드에서 명시적으로 정의되지 않은 정적 생성자의 경우 문제 해결을 위해 IL(중간 언어) 코드를 검사해야 할 수 있습니다.
  • 정적 생성자가 있으면 BeforeFieldInit 형식 특성을 추가할 수 없습니다. 이 때문에 런타임 최적화가 제한됩니다.
  • static readonly로 선언된 필드는 해당 선언의 일부로 또는 정적 생성자에서만 할당할 수 있습니다. 명시적 정적 생성자가 필요하지 않은 경우 런타임 최적화 향상을 위해 정적 생성자를 통하지 않고 선언에서 정적 필드를 초기화합니다.
  • 런타임은 단일 애플리케이션 도메인에서 정적 생성자를 1번 이하로 호출합니다. 이 호출은 클래스의 특정 형식에 따라 잠긴 영역에서 수행됩니다. 정적 생성자의 본문에는 추가 잠금 메커니즘이 필요하지 않습니다. 교착 상태 위험을 방지하려면 정적 생성자와 이니셜라이저에서 현재 스레드를 차단하지 않습니다. 예를 들어 작업, 스레드, 대기 핸들 또는 이벤트를 기다리지 않고 잠금을 획득하지 않으며 병렬 루프, Parallel.Invoke, 병렬 LINQ 쿼리와 같은 병렬 작업 차단을 실행하지 않습니다.

참고 항목

직접 액세스할 수 없더라도, 명시적 정적 생성자가 있을 경우 초기화 예외 문제 해결에 도움이 되도록 문서화해야 합니다.

사용

  • 정적 생성자는 일반적으로 클래스가 로그 파일을 사용하고, 생성자를 사용하여 이 파일에 항목을 쓰는 경우에 사용됩니다.
  • 정적 생성자는 생성자가 LoadLibrary 메서드를 호출할 수 있을 때 비관리 코드에 대한 래퍼 클래스를 만드는 경우에도 유용합니다.
  • 또한 정적 생성자를 사용하면 형식-매개 변수 제약 조건을 통해 컴파일 시간에 검사할 수 없는 형식 매개 변수에 런타임 검사를 편리하게 적용할 수 있습니다.

예시

이 예제에서 Bus 클래스에는 정적 생성자가 있습니다. Bus의 첫 번째 인스턴스를 만들 때(bus1) 정적 생성자가 호출되어 클래스를 초기화합니다. 샘플 출력은 Bus의 두 인스턴스가 생성된 경우에도 정적 생성자가 한 번만 실행되고, 인스턴스 생성자를 실행하기 전에 실행되는지 확인합니다.

public class Bus
{
    // Static variable used by all Bus instances.
    // Represents the time the first bus of the day starts its route.
    protected static readonly DateTime globalStartTime;

    // Property for the number of each bus.
    protected int RouteNumber { get; set; }

    // Static constructor to initialize the static variable.
    // It is invoked before the first instance constructor is run.
    static Bus()
    {
        globalStartTime = DateTime.Now;

        // The following statement produces the first line of output,
        // and the line occurs only once.
        Console.WriteLine("Static constructor sets global start time to {0}",
            globalStartTime.ToLongTimeString());
    }

    // Instance constructor.
    public Bus(int routeNum)
    {
        RouteNumber = routeNum;
        Console.WriteLine("Bus #{0} is created.", RouteNumber);
    }

    // Instance method.
    public void Drive()
    {
        TimeSpan elapsedTime = DateTime.Now - globalStartTime;

        // For demonstration purposes we treat milliseconds as minutes to simulate
        // actual bus times. Do not do this in your actual bus schedule program!
        Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
                                this.RouteNumber,
                                elapsedTime.Milliseconds,
                                globalStartTime.ToShortTimeString());
    }
}

class TestBus
{
    static void Main()
    {
        // The creation of this instance activates the static constructor.
        Bus bus1 = new Bus(71);

        // Create a second bus.
        Bus bus2 = new Bus(72);

        // Send bus1 on its way.
        bus1.Drive();

        // Wait for bus2 to warm up.
        System.Threading.Thread.Sleep(25);

        // Send bus2 on its way.
        bus2.Drive();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Sample output:
    Static constructor sets global start time to 3:57:08 PM.
    Bus #71 is created.
    Bus #72 is created.
    71 is starting its route 6.00 minutes after global start time 3:57 PM.
    72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/

C# 언어 사양

자세한 내용은 C# 언어 사양정적 생성자 섹션을 참조하세요.

참고 항목