begin process at 2008 05 16 21:57:34
1 173 760 membres
567 nouveaux aujourd'hui
13 972 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

PORTÉE DE LA VALEUR D'UN CHAMP STATIC


Information sur le tutorial

Catégorie :.NET Tutorial .NET ( DotNet ) Date de création : 10/02/2007 16:03:09 Vu : 2 410 fois

Note :
Aucune note

Commentaire sur cette source (2)
Ajouter un commentaire et/ou une note


Description

Précision de la portée de la valeur d'un champ static en .NET, ce qui assez important à prendre en compte notamment lors de leur utilisation pour un développement d'application Web.

Tutorial

Un champ static est un champ qui appartient à un type, et non pas à une instance de ce type, sa valeur est unique. Mais unique dans quelles limites ?
Contrairement à ce qu'on peut penser au premier abord, cette valeur n'est pas unique pour le processus complet. Pas plus qu'elle ne l'est, dans le cas d'une application web, pour une session.
Par défaut, en .NET, la portée de la valeur d'un champ static est le domaine d'application dans lequel l'assembly contenant le type a été chargé.
Ramené au cas de l'application web, cette valeur sera donc partagée par toutes les sessions de l'application, IIS créant un domaine d'application par répertoire virtuel.

Il existe cependant la possibilité de définir 3 autres portées :

  • Par processus (RVA), bien qu'à ma connaissance nous ne puissions le faire en C# ou VB.NET, par contre c'est visiblement réalisable en IL ou C++.
    Ceci dit, cette portée est limitée au types scalaires et valeur, les types référence sont exclus.
  • Par thread, par domaine d'application : l'attribut ThreadStatic permet de rendre la valeur unique par thread, et toujours par domaine d'application.
  • Par contexte, par domaine d'application : l'attribut ContextStatic permet de rendre la valeur unique par contexte, et toujours par domaine d'application.

Si vous venez à utiliser les attributs ThreadStatic ou ContextStatic, ne perdez pas de vue que le constructeur de classe n'est exécuté qu'une seule fois.
Ainsi, si vous fournissez une valeur initiale ou initialisez le champ via un constructeur de classe (ce qui revient au même une fois le code compilé), seul le premier accès déclenchera l'initialisation : en dehors du thread ayant accédé en premier au type, le champ marqué ThreadStatic possèdera sa valeur par défaut pour un type valeur, ou null pour un type référence. Vous devrez donc vous orienter sur une initialisation au premier accès, un booléen static pouvant vous servir de " témoin ", au lieu de se baser sur la valeur par défaut car après tout il peut s'agir d'une valeur à part entière.

 

Prenons comme exemple les 3 classes suivantes :

///


/// Cette classe expose un membre static "simple".
///

public class ClassA
{
    static ClassA()
    {
        // Initialisation du champ via le constructeur de classe
        // Ecrire
        // private static Guid _ID = System.Guid.NewGuid();
        // aurais eu le même effet.
        ClassA._ID = System.Guid.NewGuid();
    } 

    private static Guid _ID; 

    public static Guid ID
    {
        get
        {
            return ClassA._ID;
        }
    }
}

///


/// Cette classe expose un membre static marqué ThreadStatic,
/// que nous initialisons dans le constructeur de classe.
/// A ne pas reproduire donc.
///

public class ClassB
{
    static ClassB()
    {
        // Initialisation du champ via le constructeur de classe
        // Ecrire
        // private static Guid _ID = System.Guid.NewGuid();
        // aurais eu le même effet.
        ClassB._ID = System.Guid.NewGuid();
    }

    [ThreadStatic()]
    private static Guid _ID;

    public static Guid ID
    {
        get
        {
            return ClassB._ID;
        }
    }
}

///


/// Cette classe expose un membre static marqué ThreadStatic,
/// que nous initialisons la première demande, en préférant
/// ici l'utilisation d'un booléen à celle du test de la
/// valeur par défaut.
///

public class ClassC
{
    [ThreadStatic()]
    private static Boolean _initialized;

    [ThreadStatic()]
    private static Guid _ID;

    public static Guid ID
    {
        get
        {
            // si notre ID n'a pas encore été initialisé,
            // nous le faisons.
            if ( !ClassC._initialized )
            {
                ClassC._ID = System.Guid.NewGuid();
                ClassC._initialized = true;
            }
           
            return ClassC._ID;
        }
    }
}

Nous obtenons la sortie suivante :

Thread 1
ClassA : 15382d99-b4c5-4af3-856b-643aed3f8dd7
ClassB : 78772e37-e9db-4582-85ae-32b99335d4d3
ClassC : 859cfdb3-8726-4ce5-8ad4-52413b515b97

Thread 2
ClassA : 15382d99-b4c5-4af3-856b-643aed3f8dd7
ClassB : 00000000-0000-0000-0000-000000000000
ClassC : cddee80f-b9ac-466f-a026-cba2cd01e5e2

Nous pouvons constater que pour la classe ClassA, comme attendu, la même valeur est bien présente.
En revanche pour la classe ClassB, nous pouvons nous percevoir que le champ n?est initialisé que pour le premier Thread appelant, la classe ClassC apportant quant à elle le résultat attendu.

En effectuant le test avec chargement de l'assembly dans deux domaines d'application distincts, nous pouvons bien observer des valeurs différentes suivant l'AppDomain pour l'ID de ClassA :

AppDomain 1 - Thread 1
ClassA : 50252b92-cd87-4876-9e87-9ad4d672da53
ClassB : 9e7e9976-a7c4-4fd7-98ad-9e7a4f421d72
ClassC : 45f1ee06-ca47-4423-a96f-79305fc89225

AppDomain 1 - Thread 2
ClassA : 50252b92-cd87-4876-9e87-9ad4d672da53
ClassB : 00000000-0000-0000-0000-000000000000
ClassC : dac01dc2-0d66-44c8-ad49-fb08204ba8e0

AppDomain 2 - Thread 1
ClassA : 920dd539-b2c9-489d-a33c-b736992cbc98
ClassB : b2dd2878-dc78-4910-b2f1-e0e371884608
ClassC : 5052f3fd-69e4-4aa2-8849-df42f6266f13

AppDomain 2 - Thread 2
ClassA : 920dd539-b2c9-489d-a33c-b736992cbc98
ClassB : 00000000-0000-0000-0000-000000000000
ClassC : 2201f34b-f4d1-4a23-8058-7c18171130d0

 

Vous pouvez trouver la solution utilisée pour les tests ici.

(Initialement posté sur mon blog)

 

11 février 2007 12:10:51 :
Ajout du lien vers la solution.
11 février 2007 12:15:31 :
Correction petite faute de français.
  • signaler à un administrateur
    Commentaire de MorpionMx le 11/02/2007 10:16:48 administrateur CS

    Très bonnes explications.
    Par contre tu devrais aussi fournir les Main Methods, pour les gens qui aiment bien les exemples "tout cuits" ;)

  • signaler à un administrateur
    Commentaire de coq le 11/02/2007 11:54:18 administrateur CS

    Hum oui pas bête, je vais poster la solution complète vu que c'est séparé en 3 assemblys.

Ajouter un commentaire

Appels d'offres

Pub



CalendriCode

Mai 2008
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

VS Express FR Gratuit !

VS Express en français et 100% gratuit !

Boutique

Boutique de goodies CodeS-SourceS