정적 생성자는 어떻게 작동합니까?


82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

내가 가정 한 순서는 다음과 같습니다.

  1. 정적 생성자의 시작
  2. 정적 생성자의 끝
  3. 메인 시작
  4. MyMethod 시작
  5. 메인 끝

이제 어떤 시나리오에서든 4가 2 이전에 시작되면 나는 망했다. 가능합니까?


8
이것은 자바 또는 C # 질문입니까? 두 가지 태그를 모두 입력했는데 사양이 두 언어에서 동일하다고 생각하지 않습니다.
ARRG 2012

내 오프닝에서 이것은 둘 다 똑같은 작업입니다.하지만 저는 C # 사람입니다 .. Sry for that
om471987

4
Java에는 동일한 방식으로 정적 생성자가 없으며 정적 초기화를위한 정적 블록 만 있습니다. 정적 {// 무언가 ...}
데라즈

2
개인적으로 나는 정적 생성자 내부의 어떤 형태의 상호 작용에 대해 불편 함을 느낍니다. 나는 당신의 목표를 이해합니다 (이 정적 클래스의 모든 메소드는 실행을 허용하기 전에 사용자가 승인 될 때까지 기다리십시오).이 방법을 수행하는 것을 정말 싫어합니다.
Brian

@ 브라이언 : - 그래 ... 당신이 바로 ... 난 그냥하던되어 분석 .. 마지막으로 그냥 사용 생성자하지만 초기화 방법에하지 않기로 결정
om471987

답변:


220

여기에 한 가지 질문 만 하셨는데 물어 보셨 어야 할 질문이 열두 개 정도 있으니 모두 대답하겠습니다.

내가 가정 한 순서는 다음과 같습니다.

  1. 클래스 생성자 시작 (이라고도 함 cctor)
  2. cctor의 끝
  3. 메인 시작
  4. MyMethod 시작

이 올바른지?

아니요. 올바른 순서는 다음과 같습니다.

  1. 프로그램에 대한 cctor 시작 (있는 경우). 없기.
  2. 프로그램에 대한 cctor의 끝 (있는 경우). 없기.
  3. 메인 시작
  4. MyClass에 대한 cctor 시작
  5. MyClass에 대한 cctor의 끝
  6. MyClass.MyMethod 시작

정적 필드 이니셜 라이저가 있으면 어떻게됩니까?

CLR은 경우에 따라 정적 필드 이니셜 라이저가 실행되는 순서를 변경할 수 있습니다. 자세한 내용은 주제에 대한 Jon의 페이지를 참조하십시오.

정적 생성자와 형식 이니셜 라이저의 차이점

MyMethod해당 클래스의 cctor가 완료되기 전에 같은 정적 메서드 를 호출 할 수 있습니까?

예. cctor 자체가 MyMethod를 호출하면 cctor가 완료되기 전에 분명히 MyMethod가 호출됩니다.

cctor는 MyMethod를 호출하지 않습니다. MyMethodMyClass의 cctor가 완료되기 전에 같은 정적 메서드 를 호출 할 수 있습니까?

예. cctor가 cctor가 MyMethod를 호출하는 다른 유형을 사용하는 경우 MyClass cctor가 완료되기 전에 MyMethod가 호출됩니다.

어떤 cctor도 직접 또는 간접적으로 MyMethod를 호출하지 않습니다! 이제 MyMethodMyClass의 cctor가 완료되기 전에 같은 정적 메서드 를 호출 할 수 있습니까?

아니.

여러 스레드가 관련된 경우에도 여전히 사실입니까?

예. cctor는 모든 스레드에서 정적 메소드를 호출하기 전에 한 스레드에서 완료됩니다.

cctor를 두 번 이상 호출 할 수 있습니까? 두 개의 스레드가 모두 cctor를 실행한다고 가정하십시오.

cctor는 얼마나 많은 스레드가 관련되어 있든 관계없이 최대 한 번 호출되도록 보장됩니다. 두 스레드가 MyMethod를 "동시에"호출하면 경쟁합니다. 그들 중 하나는 MyClass cctor가 우승 스레드에서 완료 될 때까지 레이스와 블록을 잃습니다.

cctor가 완료 될 때까지 손실 스레드가 차단 됩니까? 정말 ?

정말.

그렇다면 승리 한 스레드 의 cctor가 이전에 손실 된 스레드가 가져온 잠금을 차단하는 코드를 호출하면 어떻게 될까요?

그런 다음 고전적인 잠금 순서 반전 조건이 있습니다. 프로그램 교착 상태. 영원히.

위험 해 보입니다. 교착 상태를 어떻게 피할 수 있습니까?

그렇게 할 때 아프면 그만두십시오 . cctor에서 차단할 수있는 일은 절대하지 마십시오.

복잡한 보안 요구 사항을 적용하기 위해 cctor 초기화 의미론에 의존하는 것이 좋은 생각입니까? 그리고 사용자 상호 작용을 수행하는 cctor를 갖는 것이 좋은 생각입니까?

좋은 아이디어도 아닙니다. 제 조언은 방법의 보안에 영향을 미치는 전제 조건이 충족되도록 다른 방법을 찾아야한다는 것입니다.


5
Eric,이 답변에서 "정적 생성자"를 "클래스 생성자"또는 "cctor"로 대체 한 이유가 궁금합니다. cctor를 참조 할 때 "정적 생성자"를 사용하는 것이 부적절합니까?
phoog

6
@phoog : 용어 사용에 일관성을 유지하고 싶었 기 때문에 가장 짧은 용어를 선택했습니다. "정적 생성자"와 "클래스 생성자"모두 괜찮습니다. 구현 세부 사항으로, 유형의 정적 생성자는 ".cctor"라는 특수 메소드로 방출되므로 이러한 생성자를 "a cctor"로 참조하는 것이 일반적입니다. 좀 더 공식적인 맥락에서 글을 쓰고 있다면 더 긴 용어 중 하나를 사용했습니다.
Eric Lippert 2012

@EricLippert 정적 생성자가있는 비 정적 클래스에서도 마찬가지입니까?
Legends

2
@Legends : 정적 생성자가있는 비 정적 클래스에서도 마찬가지입니까? 예.
Eric Lippert

24

MSDN 에 따르면 정적 생성자 :

정적 생성자는 첫 번째 인스턴스가 생성되거나 정적 멤버가 참조되기 전에 클래스를 초기화하기 위해 자동으로 호출됩니다.

정적 방법은 전에 정적 생성자가 호출됩니다 그래서 MyClass.MyMethod()호출 (없는 가정 정적 건설 또는 물론 정적 필드 초기화 중에 호출).

이제 비동기식으로 static constructor작업하는 경우 동기화하는 것이 귀하의 작업입니다.


7
정적 생성자에서 두 번째 스레드를 포함하는 비동기 작업을 수행하는 경우 고통의 세계에 있습니다. 교착 상태를 더 빠르게 만드는 것은 없습니다. 예제는 stackoverflow.com/a/8883117/88656 을 참조하십시오 .
Eric Lippert 2012

@Eric : 동의 ... 난하고 싶지 않겠지 만 반드시 정확하게 그 시간 MyMethod라는 의해 완성 될 싶은 있었는지 자신의 예를 ... 불렀다 형성되지 않은
제임스 마이클 헤어

11

# 3은 실제로 # 1입니다. 정적 초기화는 자신이 속한 클래스를 처음 사용할 때까지 시작되지 않습니다.

MyMethod정적 생성자 또는 정적 초기화 블록에서를 호출 하면 가능합니다 . MyMethod정적 생성자에서 직접 또는 간접적으로 호출하지 않아도 괜찮습니다.


참고 static로 최적화 적격성에 따라 처음 사용하기 전에 초기화를 실제로 호출 할 수 있음을 이해합니다 .
James Michael Hare


1
정적 생성자의 경우 true이지만 정적 초기화의 경우 내 요점이었습니다. 죄송합니다. '정적 초기화가 시작되지 않습니다 ...'라는 문구에서 nits를 선택했을 수도 있습니다. 이는 정적 생성의 경우 사실입니다.하지만 클래스에 정적 생성자가 없으면 이전에 정적 초기화가 발생할 수 있습니다.
James Michael Hare

죄송합니다. 아마도 말을 과도하게 분석 한 것 같습니다. 질문의 맥락에서 그것이 절대적으로 옳습니다. 명시적인 정적 생성자가없는 클래스의 컨텍스트에서 정적 초기화를위한 독립형 문으로서 그 문장에 대해 걱정했습니다.
James Michael Hare

@James : 당신은 과도하게 분석하지 않습니다. 여기서 용어는 중요한 차이점입니다. 정적 생성자는 C # 개념이지만 형식 초기화는 .NET입니다. 정적 생성자 (C #) 내부의 코드는 형식 이니셜 라이저 (.NET)의 일부가되지만 형식 이니셜 라이저 (즉, 의미 체계)가 트리거되는 시기방법beforefieldinit 은 C # 클래스에 정적 생성자가 있는지 여부에 따라 결정됩니다.
LukeH 2012

9

로부터 문서 (강조 광산) :

정적 생성자는 첫 번째 인스턴스 가 생성 되거나 정적 멤버가 참조 되기 전에 클래스를 초기화 하기 위해 자동으로 호출됩니다 .


2

정적 메서드에서 클래스의 인스턴스를 만들지 않은 경우 4는 항상 2 다음에 올 것이라고 보장 할 수 있지만 1과 3의 경우는 동일하지 않습니다.


2

mymethod가 실행되기 전에 정적 생성자가 호출됩니다. 그러나 2 전에 4가 호출되면 망가 졌다면 디자인을 다시 생각하는 것이 좋습니다. 어쨌든 정적 생성자에서 복잡한 작업을 수행해서는 안됩니다.


2

CLR은 정적 멤버에 액세스하기 전에 정적 생성자가 실행되도록 보장합니다. 그러나 디자인이 약간 냄새가납니다. 다음과 같이하는 것이 더 간단합니다.

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

디자인에서 인증이 실패하면 MyMethod가 실행되지 않도록하는 유일한 방법은 예외를 발생시키는 것입니다.


2

메서드가 실행되기 전에 정적 클래스의 생성자가 호출되었는지 확인합니다. 예:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

산출:

엔터 키를 치시오

// Enter를 누른 후

안녕하세요 ...

안녕!

안녕!


시스템 사용; namespace MyNameSpace {class Program {static void Main (string [] args) {Console.WriteLine ( "Entered in main"); Boop.SayHi (); Boop.SayHi (); }} 정적 클래스 Boop {정적 Boop () {Console.Read (); Console.WriteLine ( "생성자 키 입력"); } public static void SayHi () {Console.WriteLine ( "메소드가 호출 됨"); }}} 그래이 프로그램은 더 나은 이해를 제공
om471987

혹시. 다음 번에는 답변으로 게시하십시오. 그러면 더 유용하고 눈에.니다.
haiyyu

1

다음은 실제로 진행되는 순서입니다.

  1. 시작 Main
  2. 정적 MyClass생성자 시작
  3. 정적 MyClass생성자의 끝
  4. 시작 MyMethod
  5. 의 끝 Main

0

또는 디버거에서 단계별로 진행할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.