Aucune des réponses ici n'a abordé "le problème du bracketing", c'est-à-dire que les chaînes de .NET sont représentées par une combinaison d'un BStr (la longueur stockée en mémoire "avant" le pointeur) et d'un CStr (la chaîne se termine par un '\0').
La chaîne "Hello there" est donc représentée comme suit :
0B 00 00 00 48 00 65 00 6C 00 6F 00 20 00 74 00 68 00 65 00 72 00 65 00 00 00
(si elle est affectée à un char*
dans une instruction fixed
, le pointeur pointerait vers le 0x48.)
Cette structure permet une recherche rapide de la longueur d'une chaîne (utile dans de nombreux contextes) et permet au pointeur d'être passé dans un P/Invoke vers les API Win32 (ou autres) qui s'attendent à une chaîne terminée par un caractère nul.
Lorsque vous faites Substring(0, 5)
, la règle "oh, mais j'ai promis qu'il y aurait un caractère nul après le dernier caractère" dit que vous devez faire une copie. Même si vous avez la sous-chaîne à la fin, il n'y aurait pas d'endroit où mettre la longueur sans corrompre les autres variables.
Parfois, cependant, vous voulez vraiment parler "du milieu de la chaîne", et vous ne vous souciez pas nécessairement du comportement P/Invoke. La structure récemment ajoutée ReadOnlySpan
peut être utilisée pour obtenir une sous-chaîne sans copie :
string s = "Hello there";
ReadOnlySpan hello = s.AsSpan(0, 5);
ReadOnlySpan ell = hello.Slice(1, 3);
La "sous-chaîne" ReadOnlySpan
stocke la longueur de manière indépendante, et elle ne garantit pas qu'il y ait un '\0' après la fin de la valeur. Elle peut être utilisée de nombreuses manières "comme une chaîne", mais elle n'est pas "une chaîne" car elle n'a ni les caractéristiques de BStr ni de CStr (encore moins les deux). Si vous ne faites jamais (directement) de P/Invoke, il n'y a pas beaucoup de différence (à moins que l'API que vous voulez appeler n'ait pas de surcharge ReadOnlySpan
).
ReadOnlySpan
ne peut pas être utilisé comme champ d'un type de référence, donc il existe aussi ReadOnlyMemory
(s.AsMemory(0, 5)
), qui est un moyen indirect d'avoir un ReadOnlySpan
, donc les mêmes différences par rapport à une string
existent.
Certaines des réponses/commentaires sur les réponses précédentes ont parlé du gaspillage que cela représente pour le ramasse-miettes de devoir garder une chaîne d'un million de caractères alors que vous continuez à parler de 5 caractères. C'est précisément le comportement que vous pouvez obtenir avec l'approche ReadOnlySpan
. Si vous ne faites que de courtes opérations, l'approche ReadOnlySpan est probablement meilleure. Si vous devez la conserver pendant un certain temps et que vous ne conservez qu'un petit pourcentage de la chaîne d'origine, faire une véritable sous-chaîne (pour éliminer les données superflues) est probablement mieux. Il y a un point de transition quelque part au milieu, mais cela dépend de votre utilisation spécifique.
3 votes
@Mehrdad: J'aime cette question. Pourriez-vous me dire comment nous pouvons déterminer O() d'une fonction donnée en .Net ? Est-ce clair ou devrions-nous le calculer ? Merci
1 votes
@odiseh: Parfois (comme dans ce cas) il est clair que la chaîne est en cours de copie. Sinon, vous pourriez soit consulter la documentation, réaliser des tests de performances, ou essayer de consulter le code source du Framework .NET pour comprendre ce que c'est.