Il s'agit plus d'une documentation que d'une vraie question. Cela ne semble pas avoir été résolu sur SO encore (sauf si je l'ai raté), alors voici:
Imaginez une classe générique qui contient un membre statique:
class Foo<T> {
public static int member;
}
Existe-t-il une nouvelle instance du membre pour chaque classe spécifique, ou existe-t-il une seule instance pour toutes les classes de type Foo?
Il peut facilement être vérifié par un code comme celui-ci:
Foo<int>.member = 1;
Foo<string>.member = 2;
Console.WriteLine (Foo<int>.member);
Quel est le résultat et où ce comportement est-il documenté?
Un champ static
est partagé entre toutes les instances du même type. Foo<int>
et Foo<string>
sont de deux types différents. Cela peut être prouvé par la ligne de code suivante:
// this prints "False"
Console.WriteLine(typeof(Foo<int>) == typeof(Foo<string>));
Quant à savoir où cela est documenté, ce qui suit se trouve dans la section 1.6.5 Champs de la spécification du langage C # (pour C # 3):
Un champ statique identifie exactement un emplacement de stockage. Quel que soit le nombre d'instances d'une classe créées, il n'y a qu'une seule copie d'un champ statique.
Comme indiqué précédemment; Foo<int>
et Foo<string>
ne sont pas de la même classe; ce sont deux classes différentes construites à partir de la même classe générique. Comment cela se produit est décrit dans la section 4.4 du document mentionné ci-dessus:
Une déclaration de type générique, en elle-même, dénote un type générique non lié qui est utilisé comme un "plan" pour former de nombreux types différents, en appliquant des arguments de type.
Le problème ici est en fait le fait que les "classes génériques" ne sont pas du tout des classes.
Les définitions de classe génériques ne sont que des modèles pour les classes, et jusqu'à ce que leurs paramètres de type soient spécifiés, ils ne sont qu'un morceau de texte (ou une poignée d'octets).
Au moment de l'exécution, on peut spécifier un paramètre de type pour le modèle, lui donnant ainsi vie et créant une classe du type désormais entièrement spécifié. C'est pourquoi les propriétés statiques ne sont pas à l'échelle du modèle, et c'est pourquoi vous ne pouvez pas convertir entre List<string>
et List<int>
.
Cette relation reflète un peu la relation classe-objet. Tout comme les classes n'existent pas * jusqu'à ce que vous en instanciez un objet, les classes génériques n'existent pas jusqu'à ce que vous créiez une classe basée sur le modèle.
P.S. Il est tout à fait possible de déclarer
class Foo<T> {
public static T Member;
}
De cela, il est assez évident que les membres statiques ne peuvent pas être partagés, car T est différent pour différentes spécialisations.
Ils ne sont pas partagés. Je ne sais pas où il est documenté mais un avertissement d'analyse CA10 ( Ne déclarez pas de membres statiques sur les types génériques ) met en garde contre cela uniquement en raison de le risque de compliquer le code.
L'implémentation C # des génériques est plus proche de C++. Dans ces deux langues MyClass<Foo>
et MyClass<Bar>
ne partagent pas les membres statiques mais en Java ils le font. En C # et C++ MyClass<Foo>
crée en interne un type entièrement nouveau au moment de la compilation comme si les génériques étaient des macros. Vous pouvez généralement voir leurs noms générés dans la trace de la pile, comme MyClass'1
et MyClass'2
. C'est pourquoi ils ne partagent pas de variables statiques. En Java, les génériques sont implémentés par une méthode plus simple de compilation générant du code en utilisant des types non génériques et en ajoutant des conversions de type partout. Alors MyClass<Foo>
et MyClass<Bar>
ne génère pas deux classes entièrement nouvelles en Java, au lieu de cela elles sont toutes les deux la même classe MyClass
en dessous et c'est pourquoi elles partagent des variables statiques.
Ils ne sont pas vraiment partagés. Parce que le membre n'appartient pas du tout à l'instance. Un membre de classe statique appartient à la classe elle-même. Donc, si vous avez MyClass.Number, c'est la même chose pour tous les objets MyClass.Number car cela ne dépend même pas de l'objet. Vous pouvez même appeler ou modifier MyClass.Number sans aucun objet.
Mais puisque Foo <int> n'est pas la même classe que Foo <string> ces deux nombres ne sont pas partagés.
Un exemple pour montrer ceci:
TestClass<string>.Number = 5;
TestClass<int>.Number = 3;
Console.WriteLine(TestClass<string>.Number); //prints 5
Console.WriteLine(TestClass<int>.Number); //prints 3