Littéral de chaîne brute
Remarque
Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.
Il peut y avoir des différences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).
Pour en savoir plus sur le processus d'adoption des speclets de fonctionnalité dans la norme du langage C#, consultez l'article sur les spécifications.
Problème phare : https://github.com/dotnet/csharplang/issues/8647
Récapitulatif
Il commence par un minimum de trois caractères """
(sans maximum), le contenu de la chaîne (qui ne peut contenir aucun caractère new_line
), et se termine ensuite par le même nombre de guillemets avec lesquels le littéral a commencé. Exemple :
var xml = """
<element attr="content"/>
""";
Étant donné que le contenu imbriqué peut lui-même vouloir utiliser """
, les délimiteurs de début/fin peuvent être plus longs comme suit :
var xml = """"
Ok to use """ here
"""";
Pour faciliter la lecture du texte et autoriser la mise en retrait que les développeurs aiment dans le code, ces littéraux de chaîne suppriment naturellement la mise en retrait spécifiée sur la dernière ligne lors de la production de la valeur finale des littéraux. Par exemple, un littéral de la forme :
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
Contiendra :
<element attr="content">
<body>
</body>
</element>
Cela permet au code d’avoir une apparence naturelle, tout en produisant les littéraux souhaités et en évitant les coûts d’exécution si l’utilisation de routines de manipulation de chaînes spécialisées est nécessaire.
Si le comportement de mise en retrait n’est pas souhaité, il est également facile de le désactiver comme suit :
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
Un formulaire à une seule ligne est également pris en charge. Il commence par un minimum de trois caractères """
(sans maximum), le contenu de la chaîne (qui ne peut contenir aucun caractère new_line
), et se termine ensuite par le même nombre de guillemets avec lesquels le littéral a commencé. Exemple :
var xml = """<summary><element attr="content"/></summary>""";
Les chaînes brutes interpolées sont également prises en charge. Dans ce cas, la chaîne spécifie le nombre d’accolades nécessaires pour démarrer une interpolation (déterminé par le nombre de signes dollar présents au début du littéral). Toute séquence d’accolades avec moins d’accolades que cela est simplement traitée comme du contenu. Exemple :
var json = $$"""
{
"summary": "text",
"length" : {{value.Length}},
};
""";
Motivation
C# ne dispose pas d’un moyen général de créer des littéraux de chaîne simples qui peuvent contenir efficacement n’importe quel texte arbitraire. Toutes les formes de littéraux de chaînes C# aujourd’hui nécessitent une forme d’échappement au cas où le contenu utiliserait un caractère spécial (toujours si un délimiteur est utilisé). Cela empêche d’avoir facilement des littéraux contenant d’autres langages (par exemple, un littéral XML, HTML ou JSON).
Toutes les approches actuelles pour former ces littéraux en C# aujourd’hui obligent toujours l’utilisateur à échapper manuellement le contenu. Modifier à ce stade peut être très ennuyeux car l’échappement ne peut être évité et doit être traité chaque fois qu’il apparaît dans le contenu. C’est particulièrement pénible pour les regex, surtout lorsqu’elles contiennent des guillemets ou des barres obliques inverses. Même avec une chaîne verbatim (@""
), les guillemets eux-mêmes doivent être échappés, ce qui conduit à un mélange de C# et de regex entremêlés. {
et }
sont tout aussi frustrants dans les chaînes ($""
) interpolées.
Le cœur du problème est que toutes nos chaînes ont un délimiteur de début et de fin fixe. Tant que cela est le cas, nous devrons toujours avoir un mécanisme d’échappement car le contenu de la chaîne peut avoir besoin de spécifier ce délimiteur de fin dans leur contenu. Cela est particulièrement problématique, car ce délimiteur "
est extrêmement courant dans de nombreux langages.
Pour résoudre ce problème, cette proposition permet des délimiteurs de début et de fin flexibles afin qu’ils puissent toujours être intégrés de manière à ne pas entrer en conflit avec le contenu de la chaîne.
Objectifs
- Fournir un mécanisme qui permettra à toutes les valeurs de chaîne d’être fournies par l’utilisateur sans besoin de aucune séquence d’échappement. Comme toutes les chaînes doivent être représentées sans séquences d’échappement, il doit toujours être possible pour l’utilisateur de spécifier des délimiteurs qui n’entreront pas en conflit avec le contenu du texte.
- Supporter les interpolations de la même manière. Comme ci-dessus, comme toutes les chaînes doivent être représentées sans échappement, il doit toujours être possible pour l’utilisateur de spécifier un délimiteur
interpolation
qui n’entrera pas en conflit avec le contenu du texte. Il est important de noter que les langues qui utilisent nos caractères délimiteurs d'interpolation ({
et}
) devraient offrir une expérience de première classe et ne pas être pénibles à utiliser. - Les littéraux de chaînes multiligne devraient être agréables dans le code et ne pas rendre l’indentation au sein de l’unité de compilation étrange. Il est important de noter que les valeurs littérales qui elles-mêmes n'ont pas de retrait ne doivent pas être forcées d'occuper la première colonne du fichier, car cela peut perturber le flux du code et les rendrait mal alignées par rapport au reste du code qui les entoure.
- Ce comportement devrait être facile à remplacer tout en gardant les littéraux clairs et faciles à lire.
- Pour toutes les chaînes qui ne contiennent pas le caractère
new_line
et ne commencent ni ne se terminent par un guillemet ("
), il devrait être possible de représenter la chaîne littérale elle-même sur une seule ligne.- Optionnellement, avec une complexité supplémentaire, nous pourrions affiner cela pour déclarer que : Pour toutes les chaînes qui ne contiennent pas elles-mêmes de
new_line
(mais peuvent commencer ou se terminer par un caractère guillemet"
), il devrait être possible de représenter le littéral de chaîne lui-même sur une seule ligne. Pour plus d’informations, consultez la proposition développée dans la sectionDrawbacks
.
- Optionnellement, avec une complexité supplémentaire, nous pourrions affiner cela pour déclarer que : Pour toutes les chaînes qui ne contiennent pas elles-mêmes de
Conception détaillée (cas de non-interpolation)
Nous ajouterons une nouvelle string_literal
production sous la forme suivante :
string_literal
: regular_string_literal
| verbatim_string_literal
| raw_string_literal
;
raw_string_literal
: single_line_raw_string_literal
| multi_line_raw_string_literal
;
raw_string_literal_delimiter
: """
| """"
| """""
| etc.
;
raw_content
: not_new_line+
;
single_line_raw_string_literal
: raw_string_literal_delimiter raw_content raw_string_literal_delimiter
;
multi_line_raw_string_literal
: raw_string_literal_delimiter whitespace* new_line (raw_content | new_line)* new_line whitespace* raw_string_literal_delimiter
;
not_new_line
: <any unicode character that is not new_line>
;
Le délimiteur de fin pour raw_string_literal
doit correspondre au délimiteur de début. Par conséquent, si le délimiteur de début est """""
, le délimiteur de fin doit être le même.
La grammaire ci-dessus pour raw_string_literal
doit être interprétée comme suit :
- Il commence par au moins trois guillemets (mais aucune limite supérieure au niveau des guillemets).
- Il continue ensuite avec le contenu sur la même ligne que les guillemets de début. Ce contenu qui se trouve sur la même ligne peut être vides ou non. 'vide' est synonyme d’espace.
- Si le contenu de cette même ligne n’est pas vide, aucun autre contenu ne peut suivre. En d’autres termes, le littéral doit se terminer par le même nombre de guillemets sur cette même ligne.
- Si le contenu sur la même ligne est vide, alors le littéral peut continuer avec un
new_line
et un certain nombre de lignes de contenu subséquentes etnew_line
s.- Une ligne de contenu désigne n’importe quel texte à l’exception de
new_line
. - Il se termine ensuite par un
new_line
, un certain nombre (éventuellement zéro) dewhitespace
et le même nombre de guillemets avec lesquels le littéral a commencé.
- Une ligne de contenu désigne n’importe quel texte à l’exception de
Valeur du littéral de chaîne brute
Les parties entre les délimiteurs raw_string_literal_delimiter
de début et de fin sont utilisées pour former la valeur du littéral raw_string_literal
de la manière suivante :
- Dans le cas de
single_line_raw_string_literal
, la valeur du littéral sera exactement le contenu entre leraw_string_literal_delimiter
de début et de fin. - Dans le cas de
multi_line_raw_string_literal
, l’élémentwhitespace* new_line
initial et l’élémentnew_line whitespace*
final ne font pas partie de la valeur de la chaîne. Cependant, la portionwhitespace*
finale précédant le terminalraw_string_literal_delimiter
est considérée comme l’« espace d’indentation » et affectera la façon dont les autres lignes sont interprétées. - Pour obtenir la valeur finale, la séquence
(raw_content | new_line)*
est parcourue et les opérations suivantes sont effectuées :- Si c’est un
new_line
, le contenu dunew_line
est ajouté à la valeur finale de la chaîne. - S’il ne s’agit pas d’un
raw_content
'vide' (c’est-à-dire sinot_new_line+
contient un caractère autre quewhitespace
) :- L’« espace d’indentation » doit être un préfixe de
raw_content
. Sinon, une erreur se produit. - L’« espace d’indentation » est supprimé du début de
raw_content
et le reste est ajouté à la valeur finale de la chaîne.
- L’« espace d’indentation » doit être un préfixe de
- S’il s’agit d’un
raw_content
'vide' (c’est-à-dire sinot_new_line+
correspond entièrement à unwhitespace
) :- L’« espace d’indentation » doit être un préfixe de
raw_content
ou leraw_content
doit être un préfixe de l’« espace d’indentation ». Sinon, une erreur se produit. - Autant de l’« espace d’indentation » est supprimé du début de
raw_content
et tout reste est ajouté à la valeur finale de la chaîne.
- L’« espace d’indentation » doit être un préfixe de
- Si c’est un
Clarifications :
Un littéral
single_line_raw_string_literal
n’est pas en mesure de représenter une chaîne contenant une valeurnew_line
. Unsingle_line_raw_string_literal
ne participe pas à la suppression de l’« espace d’indentation ». Sa valeur correspond toujours exactement aux caractères situés entre les délimiteurs de début et de fin.Étant donné qu'un
multi_line_raw_string_literal
ignore lenew_line
final de la dernière ligne de contenu, la chaîne suivante est une chaîne sansnew_line
de départ et sansnew_line
de fin.
var v1 = """
This is the entire content of the string.
""";
Cela maintient la symétrie avec la façon dont le new_line
de début est ignoré, et cela fournit également un moyen uniforme de garantir que l’« espace d’indentation » peut toujours être ajusté. Pour représenter une chaîne avec un élément new_line
final, une ligne supplémentaire doit être fournie comme suit :
var v1 = """
This string ends with a new line.
""";
Un littéral
single_line_raw_string_literal
ne peut pas représenter une valeur de chaîne qui commence ou se termine par un guillemet ("
) bien qu’une complément à cette proposition soit fourni dans la sectionDrawbacks
qui montre comment cela peut être pris en charge.Un littéral
multi_line_raw_string_literal
commence par un élémentwhitespace* new_line
qui suit le délimiteurraw_string_literal_delimiter
initial. Le contenu après le délimiteur est entièrement ignoré et n'est pas utilisé de quelque manière dans la détermination de la valeur de la chaîne. Cela permet à un mécanisme de spécifier un littéralraw_string_literal
dont le contenu commence par un caractère"
lui-même. Exemple :
var v1 = """
"The content of this string starts with a quote
""";
- Un littéral
raw_string_literal
peut également représenter un contenu qui se termine par un guillemet ("
). Cela est supporté car le délimiteur de terminaison doit être sur sa propre ligne. Exemple :
var v1 = """
"The content of this string starts and ends with a quote"
""";
var v1 = """
""The content of this string starts and ends with two quotes""
""";
- L’exigence qu’un
raw_content
« vide » soit soit un préfixe de l’« espace d’indentation », soit que l’« espace d’indentation » soit un préfixe de celui-ci aide à garantir que des scénarios confus avec un mélange de types d’espaces ne se produisent pas, surtout qu’il serait alors peu clair ce qu’il devrait arriver avec cette ligne. Par exemple, le cas suivant est non valide :
var v1 = """
Start
<tab>
End
""";
Ici, l’« espace d’indentation » est de neuf espaces, mais le
raw_content
« vide » ne commence pas par un préfixe de cela. Il n’y a pas de réponse claire quant à la façon dont cette ligne<tab>
devrait être traitée. Devrait-on l’ignorer ? Devrait-elle être identique à.........<tab>
? Par conséquent, rendre cela illégal semble être le moyen le plus clair pour éviter toute confusion.Les cas suivants sont légaux et représentent la même chaîne :
var v1 = """
Start
<four spaces>
End
""";
var v1 = """
Start
<nine spaces>
End
""";
Dans les deux cas, l’« espace d’indentation » sera de neuf espaces. Et dans les deux cas, nous supprimerons autant de ce préfixe que possible, ce qui conduit le raw_content
« vide » dans chaque cas à être vide (sans compter chaque new_line
). Cela permet aux utilisateurs de ne pas avoir à voir l’espace sur ces lignes ni à potentiellement s’en inquiéter lorsqu’ils copient/collent ou modifient ces lignes.
- Dans le cas toutefois de :
var v1 = """
Start
<ten spaces>
End
""";
L’« espace d’indentation » sera toujours de neuf espaces. Toutefois, nous allons supprimer autant d'espaces blancs d'indentation que possible, et le raw_content
« vide » ajoutera un seul espace au contenu final. Cela permet les cas où le contenu a besoin d’espaces sur ces lignes qui doivent être préservés.
- Techniquement, ce qui suit n’est pas valide :
var v1 = """
""";
Cela est dû au fait que le début de la chaîne brute doit avoir un élément new_line
(ce qui est le cas), mais que la fin doit également avoir un élément new_line
(ce qui n’est pas le cas). Le raw_string_literal
légal minimal est :
var v1 = """
""";
Cependant, cette chaîne est décidément inintéressante car elle équivaut à ""
.
Exemples d’indentation
L’algorithme de l’« espace d’indentation » peut être visualisé sur plusieurs entrées comme suit. Les exemples suivants utilisent le caractère de barre verticale |
pour illustrer la première colonne de la chaîne brute résultante :
Exemple 1 : cas standard
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
est interprété comme
var xml = """
|<element attr="content">
| <body>
| </body>
|</element>
""";
Exemple 2 : délimiteur de fin sur la même ligne que le contenu.
var xml = """
<element attr="content">
<body>
</body>
</element>""";
Ce n’est pas valide. La dernière ligne de contenu doit se terminer par new_line
.
Exemple 3 : délimiteur de fin avant le délimiteur de début
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
est interprété comme
var xml = """
| <element attr="content">
| <body>
| </body>
| </element>
""";
Exemple 4 : délimiteur de fin après le délimiteur de début
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
Ce n’est pas valide. Les lignes de contenu doivent commencer par l’« espace d’indentation »
Exemple 5 : ligne vide
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
est interprété comme
var xml = """
|<element attr="content">
| <body>
| </body>
|
|</element>
""";
Exemple 6 : ligne vide avec moins d’espace que le préfixe (les points représentent des espaces)
var xml = """
<element attr="content">
<body>
</body>
....
</element>
""";
est interprété comme
var xml = """
|<element attr="content">
| <body>
| </body>
|
|</element>
""";
Exemple 7 : ligne vide avec plus d’espace que le préfixe (les points représentent des espaces)
var xml = """
<element attr="content">
<body>
</body>
..............
</element>
""";
est interprété comme
var xml = """
|<element attr="content">
| <body>
| </body>
|....
|</element>
""";
Conception détaillée (cas d’interpolation)
Les interpolations dans les chaînes interpolées normales (par exemple $"..."
) sont supportées aujourd’hui grâce à l’utilisation du caractère {
pour démarrer une interpolation
et l’utilisation d’une séquence d’échappement {{
pour insérer un caractère d’accolade ouvrante réel. L’utilisation de ce même mécanisme violerait les objectifs « 1 » et « 2 » de cette proposition. Les langages qui ont {
comme caractère de base (exemples : JavaScript, JSON, Regex, et même C# intégré) auraient maintenant besoin d’échappement, annulant ainsi le but des littéraux de chaînes brutes.
Pour prendre en charge les interpolations, nous les introduisons d'une manière différente de celle des chaînes interpolées normales $"
. Plus précisément, un littéral interpolated_raw_string_literal
commence par un certain nombre de caractères $
. Le nombre de ces caractères indique le nombre de caractères {
(et }
) nécessaires dans le contenu du littéral pour délimiter l’interpolation
. Il est important de noter qu’il n’existe toujours aucun mécanisme d’échappement pour les accolades. Au contraire, tout comme pour les guillemets ("
), le littéral lui-même peut toujours s’assurer qu’il spécifie des délimiteurs pour les interpolations qui ne risquent pas de entrer en collision avec le reste du contenu de la chaîne. Par exemple, un littéral JSON contenant des trous d’interpolation peut être écrit comme suit :
var v1 = $$"""
{
"orders":
[
{ "number": {{order_number}} }
]
}
"""
Ici, le {{...}}
correspond au nombre requis de deux accolades spécifié par le préfixe de délimiteur $$
. Dans le cas d’un seul $
, cela signifie que l’interpolation est spécifiée de la même manière que {...}
dans les littéraux de chaîne interpolés normaux. Il est important de noter que cela signifie qu’un littéral interpolé avec les caractères N
$
peut avoir une séquence d’accolades 2*N-1
(du même type consécutivement). Les dernières accolades N
démarreront (ou termineront) une interpolation, et les accolades N-1
restantes seront simplement du contenu. Exemple :
var v1 = $$"""X{{{1+1}}}Z""";
Dans ce cas, les deux accolades {{
et }}
internes appartiennent à l’interpolation, et les accolades singulières externes sont simplement du contenu. Ainsi, la chaîne ci-dessus équivaut au contenu X{2}Z
. Avoir 2*N
accolades (ou plus) est toujours une erreur. Pour avoir des séquences plus longues d’accolades comme contenu, le nombre de caractères $
doit être augmenté en conséquence.
Les littéraux de chaîne brute interpolés sont définis comme suit :
interpolated_raw_string_literal
: single_line_interpolated_raw_string_literal
| multi_line_interpolated_raw_string_literal
;
interpolated_raw_string_start
: $
| $$
| $$$
| etc.
;
interpolated_raw_string_literal_delimiter
: interpolated_raw_string_start raw_string_literal_delimiter
;
single_line_interpolated_raw_string_literal
: interpolated_raw_string_literal_delimiter interpolated_raw_content raw_string_literal_delimiter
;
multi_line_interpolated_raw_string_literal
: interpolated_raw_string_literal_delimiter whitespace* new_line (interpolated_raw_content | new_line)* new_line whitespace* raw_string_literal_delimiter
;
interpolated_raw_content
: (not_new_line | raw_interpolation)+
;
raw_interpolation
: raw_interpolation_start interpolation raw_interpolation_end
;
raw_interpolation_start
: {
| {{
| {{{
| etc.
;
raw_interpolation_end
: }
| }}
| }}}
| etc.
;
La définition ci-dessus est similaire à la définition de raw_string_literal
, mais avec quelques différences importantes. Un littéral interpolated_raw_string_literal
doit être interprété comme suit :
- Il commence par au moins un signe dollar (sans limite supérieure), puis trois guillemets (également sans limite supérieure).
- Il continue ensuite avec le contenu sur la même ligne que les guillemets de début. Ce contenu sur la même ligne peut être vide ou non. 'vide' est synonyme d’espace.
- Si le contenu de cette même ligne n’est pas vide, aucun autre contenu ne peut suivre. En d’autres termes, le littéral doit se terminer par le même nombre de guillemets sur cette même ligne.
- Si le contenu sur la même ligne est vide, alors le littéral peut continuer avec un
new_line
et un certain nombre de lignes de contenu subséquentes etnew_line
s.- Une ligne de contenu désigne n’importe quel texte à l’exception de
new_line
. - Une ligne de contenu peut contenir plusieurs occurrences
raw_interpolation
à n’importe quelle position. Leraw_interpolation
doit commencer avec un nombre égal d’accolades ouvrantes ({
) que le nombre de signes dollar au début du littéral. - Si l’espace de mise en retrait n’est pas vide, un élément
raw_interpolation
ne peut pas suivre immédiatement un élémentnew_line
. raw_interpolation
suit les règles normales spécifiées sous §12.8.3. Toutraw_interpolation
doit se terminer par le même nombre d’accolades fermantes (}
) que de signes dollar et d’accolades ouvrantes.- Toute
interpolation
peut elle-même contenir de nouvelles lignes tout comme uneinterpolation
dans un littéralverbatim_string_literal
normal (@""
). - Il se termine ensuite par un
new_line
, un certain nombre (éventuellement zéro) dewhitespace
et le même nombre de guillemets avec lesquels le littéral a commencé.
- Une ligne de contenu désigne n’importe quel texte à l’exception de
Le calcul de la valeur de chaîne interpolée suit les mêmes règles qu’un littéral raw_string_literal
normal, sauf qu’elle est mise à jour pour gérer les lignes contenant des éléments raw_interpolation
. La construction de la valeur de la chaîne se fait de la même manière, simplement avec les trous d’interpolation remplacés par les valeurs que ces expressions produisent à l’exécution. Si le littéral interpolated_raw_string_literal
est converti en FormattableString
, les valeurs des interpolations sont transmises dans leur ordre respectif au tableau arguments
pour FormattableString.Create
. Le reste du contenu du interpolated_raw_string_literal
après que l’« espace d’indentation » ait été supprimé de toutes les lignes sera utilisé pour générer la chaîne format
passée à FormattableString.Create
, sauf avec des contenus {N}
numérotés de manière appropriée à chaque emplacement où un raw_interpolation
est survenu (ou {N,constant}
dans le cas où son interpolation
est de la forme expression ',' constant_expression
).
Il existe une ambiguïté dans la spécification ci-dessus. Spécifiquement lorsqu’une section de {
dans le texte et {
d’une interpolation se touchent. Exemple :
var v1 = $$"""
{{{order_number}}}
"""
Cela peut être interprété comme {{ {order_number } }}
ou { {{order_number}} }
. Toutefois, comme le premier élément n’est pas valide (aucune expression C# ne pourrait commencer par {
), il serait inutile de l’interpréter de cette façon. Ainsi, nous interprétons de la seconde manière, où les accolades {
et }
les plus internes forment l’interpolation, et les accolades les plus externes forment le texte. À l’avenir, cela pourrait poser un problème si le langage supporte des expressions entourées d’accolades. Toutefois, dans ce cas, la recommandation serait d’écrire un tel cas comme suit : {{({some_new_expression_form})}}
. Ici, les parenthèses aideraient à désigner la portion d’expression du reste du littéral/de l’interpolation. Cette priorité est déjà liée à la façon dont les expressions conditionnelles ternaires doivent être encapsulées pour ne pas entrer en conflit avec le spécificateur de mise en forme/d’alignement d’une interpolation (par exemple, {(x ? y : z)}
).
Inconvénients
Les littéraux de chaînes brutes ajoutent plus de complexité au langage. Nous avons déjà de nombreuses formes de littéraux de chaînes pour de nombreux usages. Les chaînes ""
, les chaînes @""
et les chaînes $""
sont déjà très puissantes et flexibles. Mais elles n’ont toutes pas de moyen de fournir un contenu brut qui n’a jamais besoin d’échappement.
Les règles ci-dessus ne prennent pas en charge le cas de 4.a :
- ...
- Optionnellement, avec une complexité supplémentaire, nous pourrions affiner cela pour déclarer que : Pour toutes les chaînes qui ne contiennent pas elles-mêmes de
new_line
(mais peuvent commencer ou se terminer par un caractère guillemet"
), il devrait être possible de représenter le littéral de chaîne lui-même sur une seule ligne.
- Optionnellement, avec une complexité supplémentaire, nous pourrions affiner cela pour déclarer que : Pour toutes les chaînes qui ne contiennent pas elles-mêmes de
C’est parce que nous n’avons aucun moyen de savoir qu’un guillemet de début ou de fin ("
) doit appartenir au contenu et ne correspond pas au délimiteur lui-même. S'il s'agit d'un scénario important que nous voulons prendre en charge, nous pouvons ajouter une construction '''
parallèle pour accompagner la forme """
. Avec cette construction parallèle, une chaîne de ligne unique qui commence et se termine par "
peut être écrite facilement sous la forme '''"This string starts and ends with quotes"'''
avec la construction parallèle """'This string starts and ends with apostrophes'"""
. Cela peut également être utile pour aider à séparer visuellement les caractères de guillemets, ce qui peut être bénéfique lors de l’incorporation de langues qui utilisent principalement un caractère de guillemet beaucoup plus que l'autre.
Alternatives
https://github.com/dotnet/csharplang/discussions/89 couvre de nombreuses options ici. Les alternatives sont nombreuses, mais je trouve qu'elles s'égarent trop loin dans la complexité et leur mauvaise ergonomie. Cette approche privilégie la simplicité en augmentant progressivement la longueur des guillemets de début et de fin jusqu’à ce qu’il n’y ait plus de risque de conflit avec le contenu de la chaîne de caractères. Cela permet également au code que vous écrivez d’avoir une bonne indentation, tout en produisant un littéral désindente qui est ce que la plupart du code souhaite.
Une des variations potentielles les plus intéressantes est cependant l’utilisation de délimiteurs `
(ou ```
) pour ces littéraux de chaînes brutes. Cela aurait plusieurs avantages :
- Il éviterait tous les problèmes liés aux chaînes commençant ou se terminant par des guillemets.
- Cela ressemblerait à du markdown. Bien qu’en soi, cela ne soit potentiellement pas une bonne chose, car les utilisateurs pourraient s’attendre à une interprétation Markdown.
- Un littéral de chaîne brute n’aurait qu’à commencer et se terminer par un seul caractère dans la plupart des cas, et ne nécessiterait des multiples que dans le cas beaucoup plus rare de contenus contenant eux-mêmes des back-ticks.
- Il serait naturel d’étendre cela à l’avenir avec
```xml
, à nouveau semblable à Markdown. Bien sûr, cela est aussi vrai pour la forme"""
.
Dans l’ensemble cependant, l’avantage net ici semble négligeable. Conformément à l’histoire de C#, je pense que "
devrait continuer à être le délimiteur string literal
, tout comme il l’est pour @""
et $""
.
Concevoir des réunions
Problèmes ouverts à discuter Problèmes résolus :
- [x] devrions-nous avoir un formulaire à une seule ligne ? Techniquement, nous pourrions faire sans. Mais cela signifierait que les chaînes simples ne contenant pas de nouvelle ligne prendraient toujours au moins trois lignes. Je pense que nous devrions. C’est très lourd de forcer les constructions sur une seule ligne à être sur trois lignes juste pour éviter l’échappement.
Décision de conception : oui, nous aurons une forme de ligne unique.
- [x] devons-nous exiger que les multiligne commencent par une nouvelle ligne ? Je pense que nous le devrions. Cela nous donne également la possibilité de prendre en charge des éléments comme
"""xml
à l’avenir.
Décision de conception : Oui, nous exigerons que le texte à plusieurs lignes commence sur une nouvelle ligne
- [x] l’auto-désindentation devrait-elle être faite du tout ? Je pense que nous le devrions. Cela rend le code beaucoup plus agréable à regarder.
Décision de conception : oui, la désindentation automatique sera effectuée.
- [x] devons-nous restreindre les espaces blancs communs pour éviter de mélanger les types d’espaces blancs ? Je ne pense pas que nous le devrions. En effet, il existe une stratégie d’indentation courante appelée « tabulation pour l’indentation, espace pour l’alignement ». Il serait très naturel d’utiliser cela pour aligner le délimiteur de fin avec le délimiteur de début dans le cas où le délimiteur de début ne commence pas sur un arrêt de tabulation.
Décision de conception : nous n’aurons aucune restriction sur le mélange des espaces.
- [x] devrions-nous utiliser quelque chose d’autre pour les délimitations ?
`
correspondrait à la syntaxe markdown et signifierait que nous n’avons pas besoin de toujours commencer ces chaînes avec trois guillemets. Un seul suffirait pour le cas courant.
Décision de conception : nous utiliserons """
- [x] devrions-nous avoir une exigence selon laquelle le délimiteur a plus de guillemets que la séquence de guillemets la plus longue dans la valeur de la chaîne ? Techniquement, cela n’est pas nécessaire. Par exemple :
var v = """
contents"""""
"""
Il s’agit d’une chaîne avec """
comme délimiteur. Plusieurs membres de la communauté ont trouvé cela déroutant et ont suggéré que nous exigions dans un cas comme celui-ci que le délimiteur ait toujours plus de caractères. Cela correspondrait alors à ce qui suit :
var v = """"""
contents"""""
""""""
Décision de conception : oui, le délimiteur doit être plus long que n’importe quelle séquence de guillemets dans la chaîne elle-même.
C# feature specifications