Question

J'ai une question concernant Constructeurs de type dans un type de valeur . Cette question a été inspirée par quelque chose que Jeffrey Richter a écrit dans CLR via C # 3rd ED, il dit (à la page 195 - chapitre 8) que vous ne devez jamais définir réellement un constructeur de type dans un type de valeur car il y a des moments où le CLR n'appelle pas ça.

Alors, par exemple (Eh bien ... Jeffrey Richters Exemple en fait), je ne peux pas travailler, même en regardant l'IL, pourquoi le constructeur de type n'est pas appelé 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
    }
}

Ainsi, appliquer les règles suivantes pour les constructeurs de types, je ne peux tout simplement pas voir pourquoi le constructeur de type de valeur ci-dessus n'est pas appelé du tout.

  1. Je peux définir un constructeur de type valeur statique pour définir l'état initial du type.
  2. Un type ne peut avoir plus d'un constructeur - il n'y a pas de par défaut.
  3. Les constructeurs de types sont implicitement privés
  4. Le compilateur JIT vérifie si le constructeur de type du type a déjà été exécuté dans cette appdomaine. Sinon, il émet l'appel dans le code natif, sinon ce n'est pas comme il le sait que le type est déjà "initialisé".
  5. Alors ... Je ne peux tout simplement pas savoir pourquoi je ne peux pas voir que cette matrice de type est construite.

    Ma meilleure hypothèse serait que cela pourrait être:

    1. La manière dont le CLR construit un tableau de type. J'aurais pensé que le constructeur statique serait appelé lorsque le premier élément a été créé
    2. Le code dans le constructeur n'initialise pas de champs statiques afin qu'il soit ignoré. J'ai expérimenté l'initialisation des champs statiques privés dans le constructeur, mais le champ reste la valeur 0 par défaut - par conséquent, le constructeur n'est pas appelé.
    3. ou ... Le compilateur est en quelque sorte optimiser l'appel du constructeur en raison du document INT32 du public - mais c'est une hypothèse floue au mieux !!
    4. meilleures pratiques, etc. Asside, je ne suis que super intriguée par elle comme je veux pouvoir voir pour moi-même pourquoi elle ne s'appelle pas.

      Edit: J'ai ajouté une réponse à ma propre question ci-dessous, juste une citation de ce que Jeffrey Richter en dit de cela.

      Si quelqu'un a des idées, cela serait brillant. Merci beaucoup, James

Était-ce utile?

La solution

the Microsoft C # 4 Spec a légèrement changé de versions précédentes et reflète désormais plus précisément le comportement que nous voyons ici:

11.3.10 Constructeurs statiques

Constructeurs statiques pour les structures suivent la plupart des mêmes règles que pour les classes. L'exécution d'un constructeur statique pour un type de structure est déclenché par le Premier des événements suivants à se produire Dans un domaine d'application:

  • Un membre statique du type de structure est référencé.
  • Un constructeur explicitement déclaré du type de structure est appelé.

la création de valeurs par défaut (& # 167; 11.3.4) des types de struct ne déclencher le constructeur statique. (Un Exemple de ceci est la valeur initiale d'éléments dans un tableau.)

Le spécific ECMA et le Microsoft C # 3 SPEC Tous deux ont un événement supplémentaire dans cette liste: "Un membre d'instance du type de structure est référencé". Il semble donc que c # 3 était en contravention de sa propre spécification ici. La spécification C # 4 a été mise en alignement plus étroit avec le comportement réel des C # 3 et 4.

éditer ...

Après une enquête supplémentaire, il apparaît que, à peu près tout l'accès des membres de l'instance sauf l'accès au champ direct déclenchera le constructeur statique (au moins dans les implémentations Microsoft actuelles des C # 3 et 4).

de sorte que les implémentations actuelles sont plus étroitement corrélées avec les règles données dans les spécifications ECMA et C # 3 que celles de la spécification C # 4: Les règles C # 3 sont implémentées correctement lors de l'accès à tous les membres de l'instance sauf < / em> champs; Les règles C # 4 sont seulement implémentées correctement pour l'accès au champ.

(les différentes spécifications sont toutes d'accord - et apparemment correctement implémentées - en ce qui concerne les règles relatives à l'accès aux membres statiques et aux constructeurs explicitement déclarés.)

Autres conseils

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

L'exécution d'un constructeur statique pour une structure est déclenchée par le premier des événements suivants à se produire dans un domaine d'application:

  • Un membre d'instance de la structure est référencé.
  • Un membre statique de La structure est référencée.
  • Un constructeur explicitement déclaré de la la structure est appelée.

[ note : la création des valeurs par défaut (& # 167; 18.3.4) de la structure Les types ne déclenchent pas la statique constructeur. (Un exemple de ceci est la valeur initiale des éléments dans un Array.) note de fin ]

Donc, je suis d'accord avec vous que les deux dernières lignes de votre programme doivent chacune déclencher la première règle.

Après les tests, le consensus semble être qu'il déclenche systématiquement des méthodes, des propriétés, des événements et des indexeurs. Cela signifie qu'il est correct pour tous les membres d'instance explicite sauf les champs . Donc, si les règles C # 4 de Microsoft ont été choisies pour la norme, cela rendrait leur mise en œuvre aller de la plupart du temps à la plupart du temps.

Il suffit de mettre cela en tant que "réponse" afin de pouvoir partager ce que M. Richter lui-même a écrit à ce sujet (quelqu'un a-t-il un lien pour la dernière spécification CLR d'ailleurs, c'est facile à obtenir l'édition 2006, mais qu'il en trouve un Bit plus difficile d'obtenir le dernier):

Pour ce genre de choses, il est généralement préférable de regarder la spécification CLR que la spécification C #. La spécification CLR dit:

4. Si ce n'est pas marqué avant champion, alors la méthode d'initialiseur de type est exécutée à (c'est-à-dire déclenchée par):

• premier accès à n'importe quel champ statique de ce type ou

• Première invocation de toute méthode statique de ce type ou

• première invocation de toute instance ou méthode virtuelle de ce type s'il s'agit d'une valeur de valeur ou

• première invocation de tout constructeur pour ce type.

Comme aucune de ces conditions n'est satisfaite, le constructeur statique est pas invoqué. Les seules parties difficiles à noter sont que "_x" est un champ d'instance non pas un champ statique et la construction d'une gamme de structures pas invoque des constructeurs d'instance sur les éléments de tableau.

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

Mise à jour: Mon observation est que, à moins que l'état statique n'est utilisé, le constructeur statique ne sera jamais touché - quelque chose que 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 virus qui est parti car il a peu d'impact, c'est par conception, ou c'est un bogue en attente.

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

update3: Suite à un commentaire de Luch et référençant la réponse de Matthew Flaschen, la mise en œuvre et l'appel de votre propre constructeur dans la structure déclenche également le constructeur statique à appeler. Cela signifie qu'en un des trois scénarios, le comportement n'est pas ce qu'il dit sur l'étain.

Je viens d'ajouter une propriété statique au type et j'ai accédé à la propriété statique - elle a appelé le constructeur statique. Sans l'accès de la propriété statique, il suffit de créer une nouvelle instance du type, le constructeur statique n'a pas été 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 Spécifie que les constructeurs statiques sont pas appelés lorsque vous accédez simplement à des instances:

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

Je devinerais que vous créez un tableau de votre type de valeur.Donc, le nouveau mot-clé serait utilisé pour initialiser la mémoire pour le tableau.

sa valide pour dire

SomeValType i;
i._x = 5;

sans nouveau mot clé n'importe où, ce qui est essentiellement ce que vous faites ici.Étaient quelqueevaltype un type de référence, vous devriez initialiser chaque élément de votre tableau avec

array[i] = new SomeRefType();

Ceci est un comportement fou de conception de l'attribut "AvantfieldIlit" dans MSIL. Il affecte également C ++ / CLI, j'ai déposé un rapport de bogue où Microsoft a très bien expliqué pourquoi le comportement est la façon dont il est ainsi que j'ai signalé plusieurs sections de la norme linguistique qui n'était pas mise à jour pour décrire le comportement réel . Mais ce n'est pas visible publiquement. Quoi qu'il en soit, voici le mot final à partir de Microsoft (discuter d'une situation similaire en C ++ / CLI):

Puisque nous invoquons la norme Ici, la ligne de la partition I, 8.9.5 dit ceci:

si marqué avant champion puis le La méthode d'initialisateur de type est exécutée à, ou quelque temps avant, premier accès à n'importe quel champ statique défini pour cela Tapez.

Cette section passe en détail sur la façon dont une mise en œuvre de la langue peut choisir d'empêcher le comportement Vous décrivez. C ++ / CLI choisit non , plutôt qu'ils permettent au programmeur Pour ce faire s'ils le souhaitent.

Fondamentalement, puisque le code ci-dessous a absolument pas de champs statiques, le JIT est complètement correct en tout simplement pas Invoquant des constructeurs de classe statiques.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top