Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

Portée de la valeur d'un champ static

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 :

/// <summary>
/// Cette classe expose un membre static "simple".
/// </summary>
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;
        }
    }
}

/// <summary>
/// Cette classe expose un membre static marqué ThreadStatic,
/// que nous initialisons dans le constructeur de classe.
/// A ne pas reproduire donc.
/// </summary>
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;
        }
    }
}

/// <summary>
/// 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.
/// </summary>
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.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: dimanche 28 janvier 2007 15:54 par coq
Classé sous : , , ,

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01