Partager via


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

  1. 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.
  2. 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.
  3. 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.
  4. 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 section Drawbacks.

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 :

  1. Il commence par au moins trois guillemets (mais aucune limite supérieure au niveau des guillemets).
  2. 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.
  3. 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.
  4. 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 et new_lines.
    • 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) de whitespace et le même nombre de guillemets avec lesquels le littéral a commencé.

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 le raw_string_literal_delimiter de début et de fin.
  • Dans le cas de multi_line_raw_string_literal, l’élément whitespace* new_line initial et l’élément new_line whitespace* final ne font pas partie de la valeur de la chaîne. Cependant, la portion whitespace* finale précédant le terminal raw_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 du new_line est ajouté à la valeur finale de la chaîne.
    • S’il ne s’agit pas d’un raw_content 'vide' (c’est-à-dire si not_new_line+ contient un caractère autre que whitespace) :
      • 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.
    • S’il s’agit d’un raw_content 'vide' (c’est-à-dire si not_new_line+ correspond entièrement à un whitespace) :
      • L’« espace d’indentation » doit être un préfixe de raw_content ou le raw_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.

Clarifications :

  1. Un littéral single_line_raw_string_literal n’est pas en mesure de représenter une chaîne contenant une valeur new_line. Un single_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.

  2. Étant donné qu'un multi_line_raw_string_literal ignore le new_line final de la dernière ligne de contenu, la chaîne suivante est une chaîne sans new_line de départ et sans new_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.

         """;
  1. 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 section Drawbacks qui montre comment cela peut être pris en charge.

  2. Un littéral multi_line_raw_string_literal commence par un élément whitespace* new_line qui suit le délimiteur raw_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éral raw_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
         """;
  1. 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""
         """;
  1. 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
         """;
  1. 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.

  2. 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.

  1. 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.

  1. 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 :

  1. Il commence par au moins un signe dollar (sans limite supérieure), puis trois guillemets (également sans limite supérieure).
  2. 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.
  3. 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.
  4. 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 et new_lines.
    • 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. Le raw_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ément new_line.
    • raw_interpolation suit les règles normales spécifiées sous §12.8.3. Tout raw_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 une interpolation dans un littéral verbatim_string_literal normal (@"").
    • Il se termine ensuite par un new_line, un certain nombre (éventuellement zéro) de whitespace et le même nombre de guillemets avec lesquels le littéral a commencé.

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_literalaprè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 :

  1. ...
    • 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.

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 :

  1. Il éviterait tous les problèmes liés aux chaînes commençant ou se terminant par des guillemets.
  2. 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.
  3. 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.
  4. 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.