Question

Dans .NET, les chaînes sont immuables et sont des variables de type référence. Cela vient souvent comme une surprise pour les nouveaux développeurs .NET qui peuvent les confondre avec des objets de type de valeur en raison de leur comportement. Cependant, autre que la pratique de l'utilisation StringBuilder longtemps concaténation esp. en boucle, est-il une raison quelconque dans la pratique que l'on a besoin de savoir cette distinction?

Quels sont les scénarios du monde réel sont aidés ou évités par la compréhension de la distinction la valeur de référence en ce qui concerne les chaînes .NET par rapport à simplement faire semblant / les comprennent mal à être les types de valeur?

Était-ce utile?

La solution

La conception de strings était délibérément de telle sorte que vous ne devriez pas avoir trop de soucis à ce sujet en tant que programmeur. Dans de nombreux cas, cela signifie que vous pouvez simplement attribuer, déplacer, copier, modifier les chaînes sans trop penser aux conséquences complexes possibles si une autre référence à votre chaîne existait et serait changé en même temps (comme cela se produit avec des références d'objets).

Paramètres de chaîne dans un appel de méthode

(EDIT: cette section a ajouté plus tard)
Lorsque les chaînes sont transmises à une méthode, ils sont passés par référence. Quand ils ne sont lus dans le corps de la méthode, rien ne se passe spécial. Mais quand ils sont changés, une copie est créée et la variable temporaire est utilisée dans le reste de la méthode. Ce processus est appelé copy-on-write .

Qu'avez-juniors est qu'ils sont habitués au fait que les objets sont des références et ils sont modifiés dans une méthode qui change le paramètre passé. Pour faire la même chose avec des cordes, ils ont besoin d'utiliser le mot-clé ref. Cela permet en fait la référence de chaîne à modifier et retourné à la fonction appelante. Si vous ne le faites pas, la chaîne ne peut pas être modifiée par le corps de la méthode:

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

Sur StringBuilder

Cette distinction est importante, mais les programmeurs débutants sont généralement mieux de ne pas trop savoir à ce sujet. En utilisant StringBuilder lorsque vous faites beaucoup de chaîne « bâtiment » est bon, mais souvent, votre application aura beaucoup plus de poissons à frire et le petit gain de performance de StringBuilder est négligeable. Méfiez-vous des programmeurs qui vous indiquent que toutes les manipulations de chaîne devrait être fait en utilisant StringBuilder.

En règle générale très approximative du pouce: StringBuilder a un coût de création, mais appending est pas cher. Chaîne a un coût de création pas cher, mais concaténation est relativement cher. Le point tournant est autour de 400-500 concaténations, selon la taille: après, StringBuilder devient plus efficace

.

En savoir plus sur StringBuilder vs performance chaîne

EDIT: basé sur un commentaire de Konrad Rudolph, j'ai ajouté cette section

.

Si la règle précédente de base se demander, considérer les points suivants des explications un peu plus détaillées:

  • StringBuilder avec de nombreuses petites chaînes concaténation de chaînes ajoute vainc assez rapidement (30, 50 ajouter ses), mais 2μs, même 100% de gain de performance est souvent négligeable (sans danger pour certaines situations rares);
  • StringBuilder avec quelques grandes chaînes de y ajouter ses (80 caractères ou des chaînes plus grandes) vainc concaténation de chaînes seulement après des milliers, parfois centièmes de milliers d'itérations et la différence est souvent seulement quelques pourcents;
  • Le mélange des actions à cordes (remplacer, insérer, sous-chaîne, regex etc.) fait souvent à l'aide StringBuilder ou concaténation de chaîne égale;
  • Chaîne concaténation de constantes peut être optimisée à une distance par le compilateur, le CLR ou le JIT, il ne peut pas pour StringBuilder;
  • Code de mélange souvent concaténation +, StringBuilder.Append, String.Format, ToString et d'autres opérations de chaîne, en utilisant StringBuilder dans de tels cas est rarement efficace.

Alors, quand il efficace? Dans les cas où de nombreuses petites chaînes sont ajoutées, à savoir, pour sérialiser les données dans un fichier, par exemple, et lorsque vous n'avez pas besoin de changer les données « écrites » une fois « écrite » à StringBuilder. Et dans les cas où de nombreuses méthodes ont besoin d'ajouter quelque chose, parce que StringBuilder est un type de référence et les chaînes sont copiés quand ils sont modifiés.

Sur les chaînes internées

Un problème monte - non seulement avec les programmeurs juniors - quand ils essaient de faire une comparaison de référence et savoir que, parfois, le résultat est vrai, et parfois il est faux, en apparence les mêmes situations. Qu'est-il arrivé? Lorsque les chaînes ont été internées par le compilateur et ajoutés au pool statique global interné de chaînes, comparaison entredeux chaînes peuvent pointer vers la même adresse mémoire. Quand (référence!) Comparant deux chaînes égales, un interné et un non, donnera de faux. Utilisez comparaison de = ou Equals et ne jouez pas avec ReferenceEquals lorsqu'ils traitent avec des chaînes.

Sur String.Empty

Dans la même ligue correspond à un comportement étrange qui se produit parfois lors de l'utilisation String.Empty: l'String.Empty statique est toujours interné, mais une variable avec une valeur attribuée est non. Toutefois, par défaut le compilateur attribue String.Empty et le point à la même adresse de mémoire. Résultat:. Une variable de chaîne mutable, par rapport à ReferenceEquals, retourne vrai, alors que vous pourriez attendre de faux au lieu

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

En profondeur

Vous essentiellement posé des questions sur ce que les situations peuvent se produire aux non-initiés. Je pense que mon point se résume à éviter object.ReferenceEquals parce qu'il ne peut pas faire confiance quand il est utilisé avec des chaînes. La raison est que interner de chaîne est utilisée lorsque la chaîne est constante dans le code, mais pas toujours. Vous ne pouvez pas compter sur ce comportement. Bien que String.Empty et "" sont toujours internées, ce n'est pas lorsque le compilateur considère que la valeur peut être modifiée. Différentes options d'optimisation (de débogage vs libération et d'autres) vont donner des résultats différents.

faire vous avez besoin ReferenceEquals de toute façon? Avec des objets, il est logique, mais avec des cordes, il ne fonctionne pas. Enseigner tous ceux qui travaillent avec des cordes pour éviter son utilisation à moins qu'ils comprennent aussi unsafe et épinglés objets.

Performance

Quand la performance est importante, vous pouvez savoir que les chaînes sont en fait pas immuable et que en utilisant StringBuilder est pas toujours approche la plus rapide.

Beaucoup des informations que j'utilisé ici est le noreferrer"> de détail , avec un "comment" pour manipuler la chaîne en place (chaînes transformables).

Mise à jour: a ajouté un exemple de code
Mise à jour: ajouté 'en profondeur' section (espoir que quelqu'un trouve cela utile;)
Mise à jour: a ajouté quelques liens, a ajouté la section sur la chaîne params
Mise à jour: a ajouté l'estimation pour le moment de passer de chaînes à stringbuilder
Mise à jour: a ajouté une section supplémentaire sur StringBuilder vs performances String, après une remarque de Konrad Rudolph

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