Aucune des réponses ici n'a abordé "le problème du bracketing", c'est-à-dire que les chaînes en .NET sont représentées comme 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
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
(s'il est affecté à un char*
dans une fixed
instruction, le pointeur pointerait vers 0x48.)
Cette structure permet une recherche rapide de la longueur d'une chaîne (utile dans de nombreux contextes) et permet de passer le pointeur dans une API P / Invoke à Win32 (ou autre) qui attend une chaîne terminée par null.
Lorsque vous effectuez 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", la règle dit que vous devez faire une copie. Même si vous avez obtenu la sous-chaîne à la fin, il n'y aurait pas de place pour 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 ReadOnlySpan<T>
structure récemment ajoutée peut être utilisée pour obtenir une sous-chaîne sans copie:
string s = "Hello there";
ReadOnlySpan<char> hello = s.AsSpan(0, 5);
ReadOnlySpan<char> ell = hello.Slice(1, 3);
le ReadOnlySpan<char>
"sous-chaîne" stocke la longueur indépendamment, et elle ne garantit pas qu'il y a un '\ 0' après la fin de la valeur. Il peut être utilisé de plusieurs façons "comme une chaîne", mais ce n'est pas "une chaîne" car il n'a pas de caractéristiques BStr ou CStr (et encore moins les deux). Si vous n'avez jamais (directement) P / Invoke, il n'y a pas beaucoup de différence (sauf si l'API que vous souhaitez appeler n'a pas de ReadOnlySpan<char>
surcharge).
ReadOnlySpan<char>
ne peut pas être utilisé comme champ d'un type de référence, il y a donc aussi ReadOnlyMemory<char>
(s.AsMemory(0, 5)
), qui est un moyen indirect d'avoir un ReadOnlySpan<char>
, donc les mêmes différences string
existent.
Certaines des réponses / commentaires sur les réponses précédentes parlaient du gaspillage d'avoir le garbage collector à garder une chaîne d'un million de caractères pendant que vous continuez à parler de 5 caractères. C'est précisément le comportement que vous pouvez obtenir avec l' ReadOnlySpan<char>
approche. Si vous ne faites que de courts calculs, l'approche ReadOnlySpan est probablement meilleure. Si vous devez le conserver pendant un certain temps et que vous ne conserverez qu'un faible pourcentage de la chaîne d'origine, il est probablement préférable de créer une sous-chaîne appropriée (pour supprimer les données en excès). Il y a un point de transition quelque part au milieu, mais cela dépend de votre utilisation spécifique.