446 votes

En C#, pourquoi String est-il un type de référence qui se comporte comme un type de valeur ?

Une chaîne est un type de référence même si elle possède la plupart des caractéristiques d'un type de valeur, comme le fait d'être immuable et d'avoir == surchargé pour comparer le texte plutôt que de s'assurer qu'ils font référence au même objet.

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

0 votes

Étant donné que, pour les types immuables, la distinction est principalement un détail de mise en œuvre (laissant l is tests mis à part), la réponse est probablement "pour des raisons historiques". La performance de la copie ne peut pas être la raison puisqu'il n'y a pas besoin de copier physiquement des objets immuables. Maintenant, il est impossible de changer sans casser le code qui utilise effectivement is des contrôles (ou des contraintes similaires).

0 votes

BTW c'est la même réponse pour C++ (bien que la distinction entre les types valeur et référence ne soit pas explicite dans le langage), la décision de faire std::string se comporter comme une collection est une vieille erreur qui ne peut être réparée maintenant.

396voto

codekaizen Points 14819

Les chaînes de caractères ne sont pas des types de valeurs 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 jusqu'à présent) stockés sur la pile. L'allocation de chaînes sur la pile casserait toutes sortes de choses : la pile n'est que de 1MB pour 32-bit et 4MB pour 64-bit, vous devriez mettre en boîte chaque chaîne, subissant une pénalité de copie, vous ne pourriez pas internaliser les chaînes, et l'utilisation de la mémoire augmenterait, etc...

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

89 votes

Je pinaille ici, mais seulement parce que cela me donne l'occasion de faire un lien vers un article de blog pertinent pour la question : les types de valeur ne sont pas nécessairement stockés sur la pile. C'est le plus souvent vrai dans ms.net, mais pas du tout spécifié par la spécification CLI. La principale différence entre les types valeur et référence est que les types référence suivent la sémantique copy-by-value. Voir docs.microsoft.com/en-us/archive/blogs/ericlippert/ y docs.microsoft.com/en-us/archive/blogs/ericlippert/

0 votes

Sans compter que les chaînes de caractères sont de taille variable, elles ne peuvent donc pas être des types de valeur (car les types de valeur sont stockés directement là où vous les déclarez). Lorsque vous déclarez une chaîne de caractères à l'intérieur d'une classe, comment la classe pourrait-elle contenir la chaîne directement, étant donné que l'on peut changer la chaîne en une autre chaîne de caractères de longueur différente à tout moment ? Non, il faudrait qu'il y ait une RÉFÉRENCE à la chaîne de caractères car elle est de taille variable.

13 votes

@Qwertie : String n'est pas de taille variable. Lorsque vous l'ajoutez, vous créez en fait une autre String en lui allouant une nouvelle mémoire.

67voto

Jason Points 125291

Il ne s'agit pas d'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'il était transmis aux méthodes et retourné par celles-ci, etc.

Il a une valeur sémantique 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);

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

56 votes

Java n'est pas connu pour être lapidaire.

5 votes

@Matt : exactement. Quand je suis passé au C#, c'était un peu déroutant, car j'ai toujours utilisé (et je le fais encore parfois) .equals(..) pour comparer des chaînes de caractères alors que mes coéquipiers utilisaient simplement "==". Je n'ai jamais compris pourquoi ils ne laissaient pas le "==" pour comparer les références, bien que si vous réfléchissez, 90% du temps vous voudrez probablement comparer le contenu et non les références pour les chaînes de caractères.

8 votes

@Juri : En fait je pense qu'il n'est jamais souhaitable de vérifier les références, puisque parfois new String("foo"); et un autre new String("foo") peuvent être évaluées dans la même référence, ce qui n'est pas ce que l'on attendrait d'une new opérateur à faire. (Ou bien pouvez-vous me citer un cas où je voudrais comparer les références) ?

42voto

JacquesB Points 19878

Une chaîne de caractères est un type de référence avec une sémantique de valeur. Cette conception est un compromis qui permet certaines optimisations des performances.

La distinction entre les types de référence et les types de valeur est essentiellement un compromis de performance dans la conception du langage. Les types de référence ont une certaine surcharge lors de la construction, de la destruction et du ramassage des déchets, car ils sont créés sur le tas. Les types de valeur, quant à eux, ont une surcharge lors des affectations et des appels de méthode (si la taille des données est supérieure à celle d'un pointeur), car l'objet entier est copié en mémoire plutôt qu'un simple pointeur. Comme les chaînes de caractères 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, 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 de caractères.

Mais les cordes ont sémantique des valeurs ce qui signifie qu'ils sont immuables et comparés par valeur (c'est-à-dire caractère par caractère pour une chaîne de caractères), et non par comparaison de références. Cela permet certaines optimisations :

Stagiaire signifie que si plusieurs chaînes sont connues pour être égales, le compilateur peut se contenter d'utiliser une seule chaîne, ce qui économise de la mémoire. Cette optimisation ne fonctionne que si les chaînes sont immuables, sinon la modification d'une chaîne aurait des résultats imprévisibles sur les autres chaînes.

Littéraux de chaîne de caractères (qui sont connus au moment de la compilation) peuvent être internés et stockés dans une zone statique spéciale de la mémoire par le compilateur. Cela permet de gagner du temps au moment de l'exécution puisqu'il n'est pas nécessaire de les allouer et de les récupérer.

Les chaînes immuables augmentent le coût de certaines opérations. Par exemple, vous ne pouvez pas remplacer un seul caractère sur place, vous devez allouer une nouvelle chaîne pour tout changement. Mais il s'agit d'un coût minime par rapport aux avantages des optimisations.

La sémantique des valeurs masque effectivement la distinction entre les types de référence et les types de valeur pour l'utilisateur. Si un type a une sémantique de valeur, il importe peu à l'utilisateur que le type soit un type de valeur ou un type de référence - cela peut être considéré comme un détail d'implémentation.

1 votes

La distinction entre les types de valeur et les types de référence ne concerne pas vraiment les performances. Il s'agit de savoir si une variable contient un objet réel ou une référence à un objet. Une chaîne de caractères ne pourrait jamais être un type de valeur parce que la taille d'une chaîne de caractères est variable ; elle devrait être constante pour être un type de valeur ; les performances n'ont presque rien à voir avec cela. Les types de référence ne sont pas non plus très coûteux à créer.

2 votes

@Sevy : La taille d'une chaîne de caractères est constant.

1 votes

Parce qu'il contient juste une référence à un tableau de caractères, qui est de taille variable. Avoir un type de valeur dont la seule véritable "valeur" est un type de référence serait d'autant plus déroutant qu'il aurait toujours la sémantique de la référence à toutes fins utiles.

9voto

Bogdan_Ch Points 2044

Les chaînes de caractères ne sont pas les seules à être des types de référence immuables. Les délégués multi-cast aussi. C'est pourquoi il est sûr d'écrire

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

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

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

Il y a de fortes chances que les deux instances de la constante "ma chaîne" ne soient allouées qu'une seule fois dans votre assemblage.

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

Si vous devez créer une bibliothèque, où vous vous attendez à ce que de nombreuses chaînes de caractères soient passées dans vos fonctions, définissez un paramètre comme un StringBuilder ou comme un Stream.

1 votes

Il existe de nombreux exemples de types de référence immuables. Et en ce qui concerne l'exemple de la chaîne de caractères, c'est en effet à peu près garanti dans les implémentations actuelles techniquement il est est par module (pas par assemblage) - mais c'est presque toujours la même chose...

5 votes

Re le dernier point : StringBuilder n'aide pas si vous essayez de passer une chaîne de grande taille (puisqu'elle est de toute façon implémentée comme une chaîne) - StringBuilder est utile pour manipulant une chaîne de caractères plusieurs fois.

6voto

Chris Points 3290

De même, la manière dont les chaînes sont mises en œuvre (différente pour chaque plate-forme) et le moment où vous commencez à les assembler. Comme l'utilisation d'une StringBuilder . Il alloue un tampon dans lequel vous pouvez copier, et une fois que vous avez atteint la fin, il alloue encore plus de mémoire pour vous, dans l'espoir que si vous faites une grande concaténation, les performances ne seront pas entravées.

Peut-être que Jon Skeet peut aider ici ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X