문제

값 유형 내에서 형식 생성자에 관한 질문이 있습니다. 이 질문은 Jeffrey Richter가 C # 3RD ED를 통해 CLR에서 CLR에서 쓴 것으로 영감을 얻었습니다. 그것.

예를 들어 (음 ... 실제로 Jeffrey Richters 예제), 일 IL을 보면서도, 다음 코드에서 유형 생성자가 호출되지 않는 이유를 알아낼 수 없습니다.

internal struct SomeValType
{
    static SomeValType()
    {
        Console.WriteLine("This never gets displayed");
    }
    public Int32 _x;
}
public sealed class Program
{
    static void Main(string[] args)
    {
        SomeValType[] a = new SomeValType[10];
        a[0]._x = 123;
        Console.WriteLine(a[0]._x);     //Displays 123
    }
}
.

그래서, 형식 생성자 유형에 대한 다음 규칙을 적용하면 위의 값 유형 생성자가 전혀 호출되지 않는 이유를 알 수 없습니다.

  1. 정적 값 유형 생성자를 정의하여 유형의 초기 상태를 설정할 수 있습니다.
  2. 유형은 하나 이상의 생성자가 없을 수 있습니다. 기본값은 없습니다.
  3. 유형 생성자는 암시 적으로 비공개입니다
  4. JIT 컴파일러는 유형의 유형 생성자가 이미이 AppDomain에서 실행되었는지 여부를 확인합니다. 네이티브 코드로 호출을 방출하지 않으면 유형이 이미 '초기화'를 알고 있지 않으므로
  5. 그래서 ...이 유형의 배열이 생성 될 수없는 이유는 방지 할 수 없습니다.

    나의 가장 좋은 추측은 그것이 될 수 있다는 것입니다 :

    1. CLR이 형식 배열을 구성하는 방식입니다. 나는 첫 번째 항목이 생성되었을 때 정적 생성자가 호출 될 것이라고 생각했을 것입니다
    2. 생성자의 코드는 무시할 정적 필드를 초기화하지 않습니다. 생성자 내에서 개인 정적 필드를 초기화했으나 필드는 기본값 0 값으로 유지됩니다. 따라서 생성자가 호출되지 않습니다.
    3. 또는 ... 컴파일러는 공개 INT32가 설정된 것으로 인해 생성자 호출을 어떻게 최적화하지만, 그것은 최선의 퍼지 추측입니다 !!
    4. 모범 사례 등 디시 데이션, 나는 그것이 왜 호출되지 않는지 나 자신을 볼 수 있기를 원할 수 있기를 원한다면 그것은 단지 흥미 롭습니다.

      편집 : 아래의 내 자신의 질문에 대한 답변을 추가했습니다. Jeffrey Richter가 그것에 대해 말하는 것에 대한 견적을 추가했습니다.

      누구든지 아이디어가 있으면 훌륭합니다. 많은 감사, 제임스

도움이 되었습니까?

해결책

Microsoft C # 4 사양 이전 버전에서 약간 변경되었고 이제는 여기에서 보는 동작을보다 정확하게 반영합니다 :

11.3.30 정적 생성자

구조체를위한 정적 생성자는 다음과 같습니다 수업과 동일한 규칙의 대부분. 정적 생성자의 실행 struct 유형이 새로 트리거됩니다 먼저 다음 사건이 발생합니다 응용 프로그램 도메인 내에서 :

  • struct 유형의 정적 멤버가 참조됩니다.
  • 구조체 유형의 명시 적으로 선언 된 생성자가 호출됩니다.

기본값 생성 구조체 유형의 (& # 167; 11.4)는 정적 생성자를 트리거하십시오. (An. 이것의 예는 초기 값입니다 배열의 요소들.)

ecma spec /a> 및 Microsoft C # 3 사양 둘 다 해당 목록에 추가 이벤트가 있습니다. "구조체 유형의 인스턴스 멤버가 참조됩니다". C # 3가 자신의 사양을 여기에 위로하는 것처럼 보입니다. C # 4 사양은 C # 3 및 4의 실제 행동과 더 가깝게 정렬되어 왔습니다.

편집 ...

더 많은 조사 후에, 직접 필드 액세스가 정적 생성자를 트리거 할 것입니다 (최소한 C # 3 및 4의 현재 Microsoft 구현)에서 은 꽤 많이 나타납니다.

현재 구현은 C # 4 사양의 것보다 ECMA 및 C # 3 사양보다 더 가깝게 상관 관계가 있습니다. 을 제외하고는 모든 인스턴스 멤버에 액세스 할 때 C # 3 규칙이 올바르게 구현됩니다. / em> 필드; C # 4 규칙은 필드 액세스에 대해 전용 이 올바르게 구현됩니다.

(상이한 사양은 정적 멤버 액세스 및 명시 적으로 선언 된 생성자와 관련된 규칙에 관해서는 정확하게 올바르게 구현됩니다.)

다른 팁

표준의 §18.3.10 ( c # 프로그래밍 언어 책) :

struct에 대한 정적 생성자의 실행은 다음 이벤트의 첫 번째 이벤트가 응용 프로그램 도메인 내에서 발생하여 트리거됩니다.

  • struct의 인스턴스 멤버는 참조 된.
  • 정적 회원 struct가 참조됩니다.
  • 명시 적으로 선언 된 생성자 struct가 호출됩니다.

[ 참고 : 생성 struct의 기본값 (& # 167; 18.3.4) 유형이 정적을 트리거하지 않습니다 건설자. (이것의 예가 있습니다 AN.의 요소의 초기 값 배열.) end note ]

그래서 최종 두 줄이 첫 번째 규칙을 트리거 해야하는 마지막 두 줄이 귀하에게 동의합니다.

테스트 후, 합의는 메소드, 속성, 이벤트 및 인덱서에 대해 일관되게 트리거되는 것으로 보입니다. 즉, 필드를 제외한 모든 명시 적 인스턴스 멤버 에 대해 올바른 것입니다. 따라서 Microsoft의 C # 4 규칙이 표준을 위해 선택된 경우 구현이 주로 잘못된 것으로부터 잘못된 것으로 간주됩니다.

그냥 '대답'으로 이것을 넣어 richter 자신에 대해 쓴 것을 공유 할 수 있도록 (누구든지 최신 clr 사양에 대한 링크가 있지만 2006 년 에디션을 얻는 것이 쉽지만 그것을 찾는 것이 쉽습니다. 최신 정보를 얻기 위해 조금 더 어려워집니다) :

이런 종류의 물건에 대해서는 C # 사양보다 CLR 사양을 보는 것이 좋습니다. CLR 사양은 다음과 같습니다 :

4. 이전에 표시되지 않으면 해당 유형의 이니셜 라이저 메소드가 실행됩니다 (즉, 즉, 트리거) :

• 첫 번째 유형의 정적 필드에 대한 첫 번째 액세스 또는

• 그 유형의 정적 방법 또는

의 정적 방법의 첫 번째 호출

• 값 유형 또는

의 경우 해당 유형의 인스턴스 또는 가상 방법의 첫 번째 호출

• 해당 유형의 모든 생성자의 첫 번째 호출.

해당 조건 중 어느 것도 만족되지 않기 때문에 정적 생성자는 호출되지 않았습니다. 유일한 까다로운 부분은 "_x"는 정적 필드가 아닌 인스턴스 필드이며 구조체 배열을 구성하는 것은 배열 요소의 모든 인스턴스 생성자를 호출합니다.

다른 흥미로운 샘플 :

   struct S
    {
        public int x;
        static S()
        {
            Console.WriteLine("static S()");
        }
        public void f() { }
    }

    static void Main() { new S().f(); }
.

update : 정적 상태가 사용되지 않는 한 정적 생성자는 절대로 연결되지 않습니다. 런타임이 결정 유형에 결정하고 적용되지 않는 것 같습니다. 이것은 영향력이 거의 없거나 디자인이 거의 없거나 보류중인 버그입니다.

의 질문이 필요합니다.

업데이트 2 : 개인적으로, 생성자에서 펑키를하고 있지 않으면 런타임 의이 동작은 결코 문제를 일으키지 않아야합니다. 정적 상태에 액세스하면 올바르게 작동합니다.

update3 : matthew flaschen의 답변을 참조하고 구조체에서 자신의 생성자를 구현하고 호출하는 것은 정적 생성자를 트리거합니다. 세 가지 시나리오 중 하나에서 그 행동은 주석에서 그것이 말하는 것이 아닙니다.

방금 Type에 정적 속성을 추가하고 정적 생성자라고 불리는 정적 속성에 액세스했습니다. 정적 속성에 대한 액세스가 없으면 유형의 새 인스턴스를 만들기 만하면 정적 생성자가 호출되지 않았습니다.

internal struct SomeValType
    {
        public static int foo = 0;
        public int bar;

        static SomeValType()
        {
            Console.WriteLine("This never gets displayed");
        }
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // Doesn't hit static constructor
            SomeValType v = new SomeValType();
            v.bar = 1;

            // Hits static constructor
            SomeValType.foo = 3;
        }
    }
.

A 참고이 링크에서는 인스턴스에 액세스하기 만하면 가 아닌 정적 생성자가

에 액세스 할 때 호출되지 않도록 지정합니다.

http://www.jaggersoft.com/pubs/structsvsclasses.htm#default.

가치 유형의 배열을 만드는 것 같습니다.따라서 새로운 키워드는 배열의 메모리를 초기화하는 데 사용됩니다.

그 유효

SomeValType i;
i._x = 5;
.

아무데도 새로운 키워드가 없으면 본질적으로 여기에서 무엇을하고 있는지입니다.Somevaltype은 참조 유형이었습니다.

와 배열의 각 요소를 초기화해야합니다.
array[i] = new SomeRefType();
.

이것은 MSIL의 "BeforeFireFieldInit"속성의 Crazy-By-Design 동작입니다. C ++ / CLI에도 영향을줍니다. Microsoft는 그 이유가 왜 그렇게하는 방식이며 실제 동작을 설명하기 위해 동의 / 업데이트 할 필요가없는 언어 표준의 여러 섹션을 지적하는 버그 보고서를 제출했습니다. ...에 그러나 공개적으로 볼 수 없습니다. 어쨌든, Microsoft의 최종 단어가 있습니다 (C ++ / CLI에서 유사한 상황을 논의) :

우리가 표준을 호출하기 때문에

여기에서, 파티션 I의 라인, 8.9.5. 이렇게 말합니다 :

이전에 meagentfieldInit로 표시된 경우 유형의 초기화 프로그램 메소드가 실행됩니다 앞에서 또는 언젠가는 첫 번째 액세스 그것에 대해 정의 된 정적 필드에 유형.

섹션이 실제로 자세히 들어갑니다 언어 구현 방법에 대해서는 행동을 막기를 선택할 수 있습니다 당신은 묘사하고 있습니다. C ++ / CLI는 아닙니다 오히려 그들은 프로그래머를 허용합니다 그들이 원한다면 그렇게하기 위해서.

기본적으로 아래 코드는 절대적으로 정적 필드, JIT. 간단히 말해서는 완전히 정확합니다 정적 클래스 생성자를 호출합니다.

다른 언어로 표시 되더라도 동일한 행동이 보이는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top