Question

Une chaîne est un type de référence même si elle présente la plupart des caractéristiques d'un type de valeur, telles qu'être immuable et == surchargée pour comparer le texte plutôt que de s'assurer qu'elles référencent le même objet.

Pourquoi la chaîne n'est-elle pas simplement un type de valeur alors?

Était-ce utile?

La solution

Les chaînes ne sont pas des types de valeur car elles peuvent être énormes et doivent être stockées sur le tas. Les types de valeur sont (dans toutes les implémentations du CLR pour le moment) stockés dans la pile. L'allocation de chaînes par des chaînes casserait toutes sortes de choses: la pile ne représente que 1 Mo pour 32 bits et 4 Mo pour 64 bits, vous devez encadrer chaque chaîne, ce qui entraîne une pénalité de copie, vous ne pouvez pas les interner, et l'utilisation de la mémoire serait ballon, etc ...

(Edit: ajout d'une clarification sur le stockage du type de valeur étant un détail d'implémentation, ce qui conduit à cette situation où nous avons un type avec une sémantique de valeur n'héritant pas de System.ValueType. Merci Ben.)

Autres conseils

Ce n'est pas un type de valeur car les performances (espace et temps!) seraient terribles s'il s'agissait d'un type de valeur et que sa valeur devait être copiée chaque fois qu'elle était transmise à des méthodes, etc.

.

Il a une sémantique de valeur pour garder le monde sain d’esprit. Pouvez-vous imaginer combien il serait difficile de coder si

string s = "hello";
string t = "hello";
bool b = (s == t);

définir b pour être false? Imaginez à quel point il serait difficile de coder n'importe quelle application.

La distinction entre les types de référence et les types de valeur est fondamentalement un compromis de performance dans la conception du langage. Les types de référence entraînent des frais généraux pour la construction, la destruction et le ramassage des ordures, car ils sont créés sur le tas. Les types de valeur, d’autre part, ont une surcharge lors des appels de méthode (si la taille des données est supérieure à celle d’un pointeur), car tout l’objet est copié plutôt qu’un pointeur. Comme les chaînes peuvent être (et sont généralement) beaucoup plus grandes que la taille d'un pointeur, elles sont conçues comme des types de référence. En outre, comme Servy l’a souligné, la taille d’un type de valeur doit être connue au moment de la compilation, ce qui n’est pas toujours le cas pour les chaînes.

La question de la mutabilité est une question distincte. Les types de référence et les types de valeur peuvent être mutables ou immuables. Les types de valeur sont généralement immuables, car la sémantique des types de valeur mutables peut prêter à confusion.

Les types de référence sont généralement modifiables, mais peuvent être conçus comme immuables s’ils ont un sens. Les chaînes sont définies comme immuables car elles permettent certaines optimisations. Par exemple, si le même littéral est utilisé plusieurs fois dans le même programme (ce qui est assez courant), le compilateur peut réutiliser le même objet.

Alors pourquoi " == " surchargé pour comparer des chaînes de texte? Parce que c'est la sémantique la plus utile. Si deux chaînes sont égales en texte, elles peuvent ou non être la même référence à un objet en raison des optimisations. Comparer des références est donc inutile, alors que comparer un texte correspond presque toujours à vos souhaits.

En termes plus généraux, Strings a ce que l’on appelle la sémantique de la valeur . Il s'agit d'un concept plus général que celui des types de valeur, qui est un détail d'implémentation spécifique à C #. Les types de valeur ont une sémantique de valeur, mais les types de référence peuvent également avoir une sémantique de valeur. Lorsqu'un type a une sémantique de valeur, vous ne pouvez pas vraiment savoir si l'implémentation sous-jacente est un type de référence ou un type de valeur, vous pouvez donc considérer qu'il s'agit d'un détail d'implémentation.

C’est une réponse tardive à une vieille question, mais toutes les autres réponses manquent, c’est-à-dire que .NET n’avait pas de génériques jusqu’à .NET 2.0 en 2005.

String est un type de référence au lieu d'un type de valeur car il était primordial pour Microsoft de veiller à ce que les chaînes puissent être stockées de la manière la plus efficace dans les collections non génériques , telles que System.Collection.ArrayList.

Stocker un type de valeur dans une collection non générique nécessite une conversion spéciale vers le type object appelé boxing. Lorsque le CLR encadre un type de valeur, il encapsule la valeur dans un System.Object et la stocke dans le segment de mémoire géré.

La lecture de la valeur de la collection nécessite l'opération inverse appelée unboxing.

Le boxing et le déballage ont tous deux un coût non négligeable: la boxe nécessite une allocation supplémentaire, le déballage nécessite une vérification de type.

Certaines réponses prétendent à tort que string n'aurait jamais pu être implémenté en tant que type de valeur car sa taille est variable. En réalité, il est facile d'implémenter une chaîne en tant que structure de données de longueur fixe à l'aide d'une stratégie d'optimisation de petite chaîne: les chaînes seraient stockées en mémoire directement sous forme d'une séquence de caractères Unicode, à l'exception des chaînes volumineuses qui seraient stockées sous forme de pointeur vers un tampon externe. Les deux représentations peuvent être conçues pour avoir la même longueur fixe, c'est-à-dire la taille d'un pointeur.

Si les génériques existaient depuis le premier jour, la chaîne serait probablement une meilleure solution, avec une sémantique plus simple, une meilleure utilisation de la mémoire et une meilleure localisation du cache. Un List<string> contenant uniquement de petites chaînes aurait pu être un seul bloc de mémoire contigu.

Non seulement les chaînes sont des types de référence immuables. Les délégués de la diffusion multiple aussi. C’est pourquoi il est prudent d’écrire

protected void OnMyEventHandler()
{
     delegate handler = this.MyEventHandler;
     if (null != handler)
     {
        handler(this, new EventArgs());
     }
}

Je suppose que les chaînes sont immuables car c’est la méthode la plus sûre pour travailler avec elles et allouer de la mémoire. Pourquoi ne sont-ils pas des types de valeur? Les auteurs précédents ont raison en ce qui concerne la taille de la pile, etc. J'ajouterais également que transformer des chaînes en types de référence permet de réduire la taille de l'ensemble lorsque vous utilisez la même chaîne constante dans le programme. Si vous définissez

string s1 = "my string";
//some code here
string s2 = "my string";

Il est probable que les deux occurrences de & "; ma chaîne &"; constante ne sera attribuée qu'une seule fois dans votre assemblée.

Si vous souhaitez gérer les chaînes comme un type de référence habituel, insérez la chaîne dans un nouveau StringBuilder (string s). Ou utilisez MemoryStreams.

Si vous voulez créer une bibliothèque, dans laquelle vous vous attendez à ce qu'une chaîne extrêmement importante soit passée dans vos fonctions, définissez un paramètre en tant que StringBuilder ou en tant que Stream.

De plus, la manière dont les chaînes sont mises en œuvre (différentes pour chaque plate-forme) et lorsque vous commencez à les assembler. J'aime utiliser un StringBuilder. Il alloue une mémoire tampon dans laquelle vous pouvez copier, une fois que vous avez atteint la fin, il vous alloue encore plus de mémoire, dans l’espoir que, si vous effectuez une concaténation de grande taille, rien ne vous gênera.

Peut-être que Jon Skeet peut aider ici?

C’est principalement un problème de performances.

Le fait que les chaînes se comportent comme le type de valeur LIKE aide à l'écriture de code, mais le fait d'être un type de valeur aurait un impact considérable sur les performances.

Pour un examen approfondi, jetez un coup d'œil sur un article intéressant sur chaînes dans le framework .net.

Comment savoir si string est un type de référence? Je ne suis pas sûr que cela importe la façon dont cela est mis en œuvre. Les chaînes en C # sont immuables précisément pour que vous n'ayez pas à vous soucier de ce problème.

En réalité, les chaînes ont très peu de ressemblances avec les types valeur. Pour commencer, tous les types de valeur ne sont pas immuables, vous pouvez modifier la valeur d'un Int32 à votre guise et si ce serait toujours la même adresse sur la pile.

Les chaînes sont immuables pour une très bonne raison, cela n’a rien à voir avec le fait qu’il s’agit d’un type de référence, mais bien avec la gestion de la mémoire. Il est simplement plus efficace de créer un nouvel objet lorsque la taille de la chaîne change que de déplacer les éléments sur le tas géré. Je pense que vous mélangez des types valeur / référence et des concepts d’objets immuables.

Pour autant que " == " ;: Comme vous l'avez dit " == " est une surcharge d’opérateur, et encore une fois, il a été mis en place pour une très bonne raison de rendre le cadre plus utile lorsque vous travaillez avec des chaînes.

Dans un mot très simple, toute valeur ayant une taille définie peut être traitée comme un type de valeur.

N’est pas aussi simple que les chaînes sont composées de tableaux de caractères. Je regarde les chaînes comme des tableaux de caractères []. Par conséquent, ils se trouvent sur le tas, car l'emplacement de mémoire de référence est stocké sur la pile et pointe au début de l'emplacement de mémoire de la matrice sur le tas. La taille de la chaîne n'est pas connue avant d'être allouée ... parfait pour le tas.

C’est la raison pour laquelle une chaîne est vraiment immuable, car lorsque vous la modifiez, même si elle a la même taille, le compilateur ne le sait pas et doit allouer un nouveau tableau et affecter des caractères aux positions du tableau. Cela a du sens si vous considérez les chaînes comme un moyen par lequel les langues vous évitent d’allouer de la mémoire à la volée (lisez C comme une programmation)

Au risque d’obtenir un autre vote mystérieux, le fait que beaucoup mentionnent la pile et la mémoire en ce qui concerne les types de valeur et les types primitifs est dû au fait qu’ils doivent s’inscrire dans un registre du microprocesseur. Vous ne pouvez pas insérer ou extraire quelque chose dans / de la pile s'il faut plus de bits que dans un registre. Les instructions sont, par exemple, & "Pop eax &"; - car eax a une largeur de 32 bits sur un système 32 bits.

Les types de primitifs à virgule flottante sont gérés par la FPU, d’une largeur de 80 bits.

Tout cela a été décidé bien avant qu’un langage POO ne rende obscurcie la définition du type primitif et je suppose que ce type de valeur est un terme créé spécifiquement pour les langages POO.

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