38 votes

Pourquoi le CLR n'appelle-t-il pas toujours les constructeurs de type valeur

J'ai une question concernant le type de constructeurs dans un type de Valeur. Cette question a été inspiré par quelque chose que Jeffrey Richter a écrit dans le CLR via C# 3e éd, il a dit (à la page 195 - chapitre 8) que vous ne devriez jamais réellement définir un constructeur de type à l'intérieur d'un type de la valeur qu'il y a des moments où le CLR ne sera pas l'appeler.

Ainsi, par exemple (bien...Jeffrey Richters exemple), je ne peux pas savoir, même en regardant le IL, pourquoi le constructeur de type n'est pas appelée dans le code suivant:

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
    }
}

Donc, en appliquant les règles suivantes pour le type des constructeurs, je ne peux pas voir pourquoi le type de la valeur constructeur ci-dessus n'est pas appelée à tous.

  1. Je peux définir une valeur statique type de constructeur pour définir l'état initial du type.
  2. Un type ne peut pas avoir plus qu'un seul constructeur - il n'existe pas par défaut.
  3. Type de constructeurs sont implicitement privé
  4. Le compilateur JIT vérifie si le type du constructeur de type a déjà été exécutés dans ce domaine d'application. Si non, il émet l'appel en code natif, sinon ce n'est pas comme il sait le type est déjà initialisé'.

Alors...j'ai juste ne peux pas savoir pourquoi je ne peux pas voir ce type de tableau en cours de construction.

Ma meilleure supposition serait que cela pourrait être:

  1. La façon dont le CLR construit un tableau de type. J'aurais pensé que le constructeur statique sera appelé lorsque le premier élément a été créé
  2. Le code dans le constructeur n'est pas l'initialisation des champs statiques de sorte qu'il est ignoré. J'ai expérimenté avec de l'initialisation statique privé de champs dans le constructeur, mais le champ reste la valeur par défaut de 0, donc le constructeur n'est pas appelé.
  3. Ou...le compilateur est en quelque sorte l'optimisation de loin l'appel du constructeur en raison du public Int32 d'être ensemble - mais qu'est floue deviner au mieux!!

Meilleures pratiques, etc asside, je suis juste super intrigué par elle que je veux être en mesure de voir par moi-même pourquoi il n'est pas appelée.

EDIT: j'ai ajouté une réponse à ma propre question ci-dessous, juste une citation de ce que Jeffrey Richter dit à ce sujet.

Si quelqu'un a des idées, ce serait génial. Merci beaucoup, James

17voto

LukeH Points 110965

Le Microsoft C#4 Spec a légèrement changé depuis les versions précédentes et maintenant, reflète avec plus de précision le comportement que l'on voit ici:

11.3.10 constructeurs Statiques

Constructeurs statiques pour les structures suivre la plupart des mêmes règles que pour les classes. L'exécution d'un constructeur statique pour un type struct est déclenchée par l' premier des événements suivants à survenir au sein d'un domaine d'application:

  • Un membre statique de la structure type est référencé.
  • Un déclarées explicitement le constructeur de la struct type est appelé.

La création de valeurs par défaut (§11.3.4) de struct types de ne pas déclencher le constructeur statique. (Un exemple de cela est la valeur initiale d'éléments dans un tableau.)

L' ECMA Spec et le Microsoft C#3 Spec les deux ont un supplément d'événement dans la liste: "Un membre de l'instance de type struct est référencé". Il semble donc que si C#3 a été en violation de son propre spec ici. Le C#4 Spec a été mis en rapprochement avec le comportement réel de C#3 et 4.

EDIT...

Après enquête, il apparaît que pratiquement tous les membre de l'instance de l'accès à l'exception de l'accès direct va déclencher le constructeur statique (au moins dans le courant de Microsoft implémentations de C#3 et 4).

Donc, les implémentations actuelles sont plus étroitement corrélée avec les règles données dans l'ECMA et C#3 spécifications que ceux dans le C#4 spec: le C#3 règles sont correctement mis en œuvre lors de l'accès à tous les membres de l'instance à l'exception des champs; le C#4 règles sont seulement mis en œuvre correctement pour accéder au terrain.

(Les spécifications différentes sont tous d'accord -- et apparemment correctement mis en œuvre, lorsqu'il s'agit des règles relatives à la statique de l'accès des membres et explicitement déclaré constructeurs.)

11voto

Matthew Flaschen Points 131723

De §18.3.10 de la norme (voir aussi Le langage de programmation C# livre):

L'exécution d'un constructeur statique pour une structure (struct) est déclenché par le premier des événements suivants se produisent à l'intérieur d'un domaine d'application:

  • Un membre de l'instance de la structure est référencé.
  • Un membre statique de la structure est référencé.
  • Un déclarées explicitement le constructeur de la struct est appelé.

[Note: La création des valeurs par défaut (§18.3.4) de struct les types ne déclenche pas la statique constructeur. (Un exemple de ceci est la valeur initiale d'éléments dans un tableau.) la note de fin]

Donc, je serais d'accord avec vous que les deux dernières lignes de votre programme à chaque déclenchement de la première règle.

Après le test, le consensus semble être que de manière cohérente des déclencheurs pour des méthodes, des propriétés, des événements et des indexeurs. Cela signifie que c'est correct pour tous explicite les membres de l'instance à l'exception des champs. Donc si Microsoft C# 4 règles ont été choisis pour le standard, qui rendrait leur mise en œuvre vont de la plupart droit à la plupart de mal.

1voto

Adam Houldsworth Points 38632

Mise à jour: mon observation est que si l'état statique est utilisé, le constructeur statique ne sera jamais touché - quelque chose de l'exécution semble décider et ne s'applique pas aux types de référence. Cela pose la question si c'est un bug de gauche parce qu'il a peu d'impact, c'est de par leur conception, ou il est en attente d'un bug.

Mise à jour 2: personnellement, sauf si vous faites quelque chose de funky dans le constructeur, ce comportement à partir de l'exécution ne doit jamais provoquer un problème. Dès que vous accédez à l'état statique, il se comporte correctement.

Update3: suite à un commentaire par LukeH, et le référencement Matthieu Flaschen réponse, la mise en œuvre et de l'appel de votre propre constructeur de la struct déclenche également le constructeur statique pour être appelé. Ce qui signifie que dans l'un des trois scénarios, le comportement n'est pas ce qu'il dit sur l'étain.

J'ai juste ajouté une propriété statique du type et accessible que la propriété statique - il appelé le constructeur statique. Sans l'accès à la propriété statique, tout en créant une nouvelle instance de type, le constructeur statique n'était pas appelé.

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;
        }
    }

Une note dans ce lien indique que les constructeurs statiques sont pas appelée lorsque le simple accès instances:

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

1voto

Denis Palnitsky Points 6126

Un autre échantillon intéressant:

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

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

0voto

Ben Voigt Points 151460

C'est fou de par sa conception le comportement "beforefieldinit" attribut dans MSIL. Elle affecte le C++/CLI trop, j'ai déposé un rapport de bogue où Microsoft est très bien expliqué pourquoi le comportement est la façon dont il est et je l'ai souligné à plusieurs articles dans la langue standard qui ne sont pas d'accord / doivent être mis à jour pour décrire le comportement réel. Mais il n'est pas visible publiquement. De toute façon, voici le mot de la fin à partir de Microsoft (à débattre d'une situation similaire en C++/CLI):

Puisque nous sommes en invoquant la norme ici, la ligne de Partition I, 8.9.5 dit ceci:

Si marquée BeforeFieldInit puis l' type de l'initialiseur d'exécution de la méthode à, ou un peu avant, le premier accès pour tout champ statique définie pour que type.

Cette section va en fait dans le détail comment un langage de mise en œuvre pouvez choisir d'éviter le problème vous décrivez. C++/CLI choisit de ne pas pour, plutôt, ils permettent au programmeur pour ce faire, si elles le souhaitent.

En gros, depuis que le code ci-dessous a absolument aucun des champs statiques, le JIT est tout à fait correct en tout simplement pas l'invocation statique de constructeurs de classe.

Le même comportement est ce que vous voyez, bien que dans une langue différente.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X