Partilhar via


Literal de cadeia de caracteres bruta

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Language Design Meeting (LDM).

Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .

Resumo

Permita uma nova forma de literal de cadeia de caracteres que comece com um mínimo de três """ caracteres (mas não no máximo), opcionalmente seguido por um new_line, o conteúdo da cadeia de caracteres e, em seguida, termine com o mesmo número de aspas com o qual o literal começou. Por exemplo:

var xml = """
          <element attr="content"/>
          """;

Como o próprio conteúdo aninhado pode querer usar """, os delimitadores de início e fim poderiam ser mais longos da seguinte forma:

var xml = """"
          Ok to use """ here
          """";

Para facilitar a leitura do texto e permitir a indentação que os programadores preferem no código, esses literais de texto removerão naturalmente a indentação especificada na última linha ao produzir o valor literal final. Por exemplo, um literal da forma:

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
          """;

Terá o conteúdo:

<element attr="content">
  <body>
  </body>
</element>

Isso permite que o código pareça natural, enquanto ainda produz literais desejados e evita custos de tempo de execução se isso exigir o uso de rotinas especializadas de manipulação de cadeia de caracteres.

Se o comportamento de indentação não for desejado, é fácil desativá-lo desta forma:

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
""";

Um formulário de linha única também é suportado. Ele começa com um mínimo de três caracteres """ (mas sem um máximo definido), o conteúdo da cadeia (que não pode conter nenhum carácter new_line) e, em seguida, termina com o mesmo número de aspas com que começou. Por exemplo:

var xml = """<summary><element attr="content"/></summary>""";

Cadeias de caracteres interpoladas brutas também são suportadas. Nesse caso, a string especifica o número de chaves necessárias para iniciar uma interpolação (determinado pelo número de cifrões presentes no início do literal). Qualquer sequência de chaves com menos chaves do que isso é apenas tratada como conteúdo. Por exemplo:

var json = $$"""
             {
                "summary": "text",
                "length" : {{value.Length}},
             };
             """;

Motivação

C# não tem uma maneira genérica de criar literais de string simples que possam conter efetivamente qualquer texto arbitrário. Todas as formas literais de strings C# precisam hoje de algum tipo de escape caso o conteúdo use um caractere especial (sempre que um delimitador é usado). Isso evita que facilmente haja literais contendo outras linguagens (por exemplo, um literal XML, HTML ou JSON).

Todas as abordagens atuais para formar esses literais em C# hoje sempre forçam o usuário a escapar manualmente do conteúdo. A edição nesse momento pode ser altamente irritante, pois a fuga não pode ser evitada e deve ser tratada sempre que surgir no conteúdo. Isto é particularmente doloroso para regexes, especialmente quando contêm aspas ou barras invertidas. Mesmo com uma string literal (@""), as próprias citações devem ser escapadas, levando a uma mistura de C# e regex intercaladas. { e } são igualmente frustrantes em cordas interpoladas ($"").

O cerne do problema é que todas as nossas cadeias de caracteres têm um delimitador de início/fim fixo. Enquanto esse for o caso, sempre teremos que ter um mecanismo de fuga, pois o conteúdo da string pode precisar especificar esse delimitador final em seu conteúdo. Isto é particularmente problemático, uma vez que esse delimitador " é extremamente comum em muitas línguas.

Para resolver este problema, a presente proposta permite delimitadores de início e fim flexíveis para que possam sempre ser feitos de uma forma que não entre em conflito com o conteúdo da cadeia de caracteres.

Objetivos

  1. Forneça um mecanismo que permita todos os valores de cadeia de caracteres sejam fornecidos pelo usuário sem a necessidade de qualquer sequências de escape. Como todas as cadeias de caracteres devem ser representáveis sem sequências de escape, sempre deve ser possível para o usuário especificar delimitadores que terão a garantia de não colidir com nenhum conteúdo de texto.
  2. Suporte interpolações da mesma forma. Como acima, como todas as cadeias de caracteres devem ser representáveis sem escapes, deve sempre ser possível para o usuário especificar um delimitador de interpolation que será garantido para não colidir com qualquer conteúdo de texto. É importante ressaltar que as línguas que usam nossos interpolação caracteres delimitadores ({ e }) devem parecer de primeira classe e não dolorosas de usar.
  3. Literais de string de várias linhas devem parecer agradáveis no código e não devem fazer com que a indentação dentro da unidade de compilação pareça estranha. É importante ressaltar que os valores literais que não têm recuo não devem ser forçados a ocupar a primeira coluna do arquivo, pois isso pode quebrar o fluxo de código e parecerá desalinhado com o resto do código que o rodeia.
    • Esse comportamento deve ser fácil de substituir, mantendo os literais claros e fáceis de ler.
  4. Para todas as cadeias que não contenham em si mesmas um new_line ou que não iniciem ou terminem com um caractere de aspas ("), deve ser possível representar o literal da própria cadeia numa única linha.
    • Opcionalmente, com complexidade extra, poderíamos refinar isso para afirmar que: Para todas as cadeias de caracteres que não contêm uma new_line (mas podem começar ou terminar com uma citação " caractere), deve ser possível representar o próprio literal da cadeia de caracteres em uma única linha. Para mais pormenores, ver a proposta alargada na secção Drawbacks.

Projeto detalhado (caso sem interpolação)

Iremos adicionar uma nova produção string_literal com a seguinte forma:

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>
    ;

O delimitador final de um raw_string_literal deve corresponder ao delimitador inicial. Então, se o delimitador inicial é """"" o delimitador final deve ser isso também.

A gramática acima para um raw_string_literal deve ser interpretada como:

  1. Começa com pelo menos três aspas (mas sem limite superior entre aspas).
  2. Em seguida, continua com o conteúdo na mesma linha das aspas de abertura. Esses conteúdos na mesma linha podem estar em branco ou não em branco. «Em branco» é sinónimo de «espaço inteiramente em branco».
  3. Se o conteúdo nessa mesma linha não estiver em branco, nenhum outro conteúdo poderá seguir. Em outras palavras, é necessário que o literal termine com o mesmo número de aspas nessa mesma linha.
  4. Se o conteúdo na mesma linha estiver em branco, o literal pode continuar com um new_line e algum número de linhas de conteúdo subsequentes e new_lines.
    • Uma linha de conteúdo é qualquer texto, exceto um new_line.
    • Em seguida, termina com um new_line algum número (possivelmente zero) de whitespace e o mesmo número de aspas com que o literal começou.

Valor literal da cadeia de caracteres bruta

As porções entre o raw_string_literal_delimiter inicial e final são usadas para formar o valor do raw_string_literal da seguinte maneira:

  • No caso de single_line_raw_string_literal o valor do literal será exatamente o conteúdo entre o início e o fim raw_string_literal_delimiter.
  • No caso de multi_line_raw_string_literal o whitespace* new_line inicial e o new_line whitespace* final não fazem parte do valor da cadeia de caracteres. No entanto, a porção final do whitespace* que antecede o terminal raw_string_literal_delimiter é considerada o "espaço em branco de indentação" e afetará a forma como as outras linhas são interpretadas.
  • Para obter o valor final, a sequência de (raw_content | new_line)* é percorrida e o seguinte é executado:
    • Se for um new_line o conteúdo do new_line é adicionado ao valor final da cadeia de caracteres.
    • Se não se tratar de um raw_content «em branco» (ou seja, not_new_line+ contenha um carácter nãowhitespace):
      • o 'espaço em branco de indentação' deve ser o prefixo do raw_content. Caso contrário, é um erro.
      • O 'espaço em branco utilizado para recuo' é removido do início do raw_content, e o restante é adicionado ao valor final da cadeia de caracteres.
    • Se se tratar de um raw_content «em branco» (ou seja, not_new_line+ é inteiramente whitespace):
      • o 'espaço em branco de recuo' deve ser um prefixo do raw_content ou o raw_content deve ser um prefixo do 'espaço em branco de recuo'. Caso contrário, é um erro.
      • como grande parte do 'espaço em branco de recuo' é removido desde o início do raw_content e qualquer restante é adicionado ao valor final da cadeia de caracteres.

Esclarecimentos:

  1. Um single_line_raw_string_literal não é capaz de representar uma cadeia de caracteres com um valor new_line nele. Um single_line_raw_string_literal não está envolvido na remoção de "espaço em branco de indentação". Seu valor é sempre os caracteres exatos entre os delimitadores inicial e final.

  2. Como um multi_line_raw_string_literal ignora o new_line final da última linha de conteúdo, o seguinte representa uma cadeia de caracteres sem new_line inicial e sem new_line de término

var v1 = """
         This is the entire content of the string.
         """;

Isso mantém a simetria com a forma como o new_line inicial é ignorado e também fornece uma maneira uniforme de garantir que o "espaço em branco de recuo" sempre possa ser ajustado. Para representar uma cadeia de caracteres com um terminal new_line uma linha extra deve ser fornecida assim:

var v1 = """
         This string ends with a new line.

         """;
  1. Um single_line_raw_string_literal não pode representar um valor de cadeia de caracteres que começa ou termina com uma citação ("), embora um aumento para esta proposta seja fornecido na seção Drawbacks que mostra como isso pode ser suportado.

  2. Um multi_line_raw_string_literal começa com whitespace* new_line após o raw_string_literal_delimiterinicial. Esse conteúdo após o delimitador é totalmente ignorado e não é usado de forma alguma ao determinar o valor da cadeia de caracteres. Isso permite um mecanismo para especificar um raw_string_literal cujo conteúdo começa com um caractere ". Por exemplo:

var v1 = """
         "The content of this string starts with a quote
         """;
  1. Um raw_string_literal também pode representar conteúdo que termina com uma citação ("). Isso é suportado, pois o delimitador de terminação deve estar em sua própria linha. Por exemplo:
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. O requisito de que um 'blank' raw_content seja um prefixo do "espaço em branco de indentação" ou que o "espaço em branco de indentação" deva ser um prefixo dele ajuda a garantir que cenários confusos com espaços em branco mistos não ocorram, especialmente porque, de outra forma, seria pouco claro o que deveria ser feito com essa linha. Por exemplo, o seguinte caso é ilegal:
var v1 = """
         Start
<tab>
         End
         """;
  1. Aqui, o 'espaço em branco de recuo' consiste em nove caracteres de espaço, mas o 'espaço vazio' raw_content não começa com esse prefixo. Não há uma resposta clara sobre a forma como a linha <tab> deve ser tratada. Deve ser ignorado? Será que deve ser o mesmo que .........<tab>? Como tal, torná-lo ilegal parece o mais claro para evitar confusão.

  2. Os seguintes casos são legais e representam a mesma cadeia de caracteres:

var v1 = """
         Start
<four spaces>
         End
         """;
var v1 = """
         Start
<nine spaces>
         End
         """;

Em ambos os casos, o "espaço em branco de recuo" será de nove espaços. E em ambos os casos, removeremos o máximo possível desse prefixo, levando o raw_content 'em branco' em cada caso a ficar vazio (sem contar todos os new_line). Isso permite que os usuários não precisem ver e potencialmente se preocupar com o espaço em branco nessas linhas quando copiam/colam ou editam essas linhas.

  1. Contudo, no caso de:
var v1 = """
         Start
<ten spaces>
         End
         """;

O espaçamento de recuo ainda será de nove espaços. Porém, aqui removeremos o máximo possível do "espaço de indentação", e o "raw_content em branco" contribuirá com um único espaço para o conteúdo final. Isso permite casos em que há necessidade de o conteúdo ter espaço em branco nessas linhas, o qual deve ser preservado.

  1. Tecnicamente, não é legal:
var v1 = """
         """;

Isso ocorre porque o início da string bruta deve ter um new_line (o que ele faz), mas o final também deve ter um new_line (o que não tem). O valor jurídico mínimo de raw_string_literal é:

var v1 = """

         """;

No entanto, esta string é decididamente desinteressante, pois é equivalente a "".

Exemplos de indentação

O algoritmo 'indentation whitespace' pode ser visualizado em várias entradas desta forma. Os exemplos a seguir usam o caractere de barra vertical | para ilustrar a primeira coluna na cadeia de caracteres bruta resultante:

Exemplo 1 - Caso padrão

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
          """;

é interpretada como

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |</element>
           """;

Exemplo 2 - Delimitador final na mesma linha do conteúdo.

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>""";

Isto é ilegal. A última linha de conteúdo deve terminar com um new_line.

Exemplo 3 - Delimitador final antes do delimitador de início

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
""";

é interpretada como

var xml = """
|          <element attr="content">
|            <body>
|            </body>
|          </element>
""";

Exemplo 4 - Delimitador final após delimitador de início

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
              """;

Isto é ilegal. As linhas de conteúdo devem começar com o "espaço em branco de recuo"

Exemplo 5 - Linha em branco vazia

var xml = """
          <element attr="content">
            <body>
            </body>

          </element>
          """;

é interpretada como

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |
          |</element>
           """;

Exemplo 6 - Linha em branco com menos espaço em branco do que o prefixo (os pontos representam espaços)

var xml = """
          <element attr="content">
            <body>
            </body>
....
          </element>
          """;

é interpretada como

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |
          |</element>
           """;

Exemplo 7 - Linha em branco com mais espaço em branco do que prefixo (pontos representam espaços)

var xml = """
          <element attr="content">
            <body>
            </body>
..............
          </element>
          """;

é interpretada como

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |....
          |</element>
           """;

Design detalhado (caso de interpolação)

Interpolações em cadeias interpoladas normais (por exemplo, $"...") são suportadas hoje através do uso do caractere { para iniciar um interpolation e do uso de uma sequência de escape {{ para inserir um caractere de chave aberta real. A utilização deste mesmo mecanismo violaria os objetivos «1» e «2» da presente proposta. Linguagens que têm { como um caractere central (exemplos sendo JavaScript, JSON, Regex e até mesmo C# incorporado) agora precisariam escapar, desfazendo o propósito de literais de cadeia de caracteres brutos.

Para suportar interpolações, introduzimo-las de uma maneira diferente do habitual $" cadeias interpoladas. Especificamente, um interpolated_raw_string_literal começará com um certo número de caracteres $. A contagem destes indica quantos caracteres { (e }) são necessários no conteúdo do literal para delimitar o interpolation. É importante ressaltar que continua a não haver nenhum mecanismo de fuga para aparelhos encaracolados. Em vez disso, tal como acontece com aspas ("), o próprio literal pode assegurar que especifica delimitadores para as interpolações que certamente não entram em conflito com nenhuma outra parte do conteúdo da cadeia. Por exemplo, um literal JSON contendo orifícios de interpolação pode ser escrito assim:

var v1 = $$"""
         {
            "orders": 
            [
                { "number": {{order_number}} }
            ]
         }
         """

Aqui, o {{...}} corresponde à contagem necessária de duas chaves especificadas pelo prefixo do delimitador $$. No caso de um único $, isso significa que a interpolação é especificada exatamente como {...} em literais de cadeia interpolada normais. É importante ressaltar que isso significa que um literal interpolado com N$ caracteres pode ter uma sequência de 2*N-1 chaves (do mesmo tipo numa fila). Os últimos N chaves iniciarão (ou terminarão) uma interpolação, e os N-1 restantes serão apenas conteúdo. Por exemplo:

var v1 = $$"""X{{{1+1}}}Z""";

Neste caso, as chaves internas {{ e }} pertencem à interpolação, e as chaves exteriores singulares são apenas conteúdo. Portanto, a string acima é equivalente ao conteúdo X{2}Z. Ter 2*N (ou mais) chavetas é sempre um erro. Para ter sequências mais longas de chaves como conteúdo, o número de caracteres $ deve ser aumentado de acordo.

Os literais de cadeia bruta interpolados são definidos como:

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

O acima exposto é semelhante à definição de raw_string_literal mas com algumas diferenças importantes. Um interpolated_raw_string_literal deve ser interpretado como:

  1. Começa com pelo menos um cifrão (mas sem limite superior) e depois três aspas (também sem limite superior).
  2. Em seguida, continua com o conteúdo na mesma linha das aspas de abertura. Este conteúdo na mesma linha pode estar em branco ou não em branco. «Em branco» é sinónimo de «espaço inteiramente em branco».
  3. Se o conteúdo nessa mesma linha não estiver em branco, nenhum outro conteúdo poderá seguir. Em outras palavras, é necessário que o literal termine com o mesmo número de aspas nessa mesma linha.
  4. Se o conteúdo na mesma linha estiver em branco, o literal pode continuar com um new_line e algum número de linhas de conteúdo subsequentes e new_lines.
    • Uma linha de conteúdo é qualquer texto, exceto um new_line.
    • Uma linha de conteúdo pode conter várias ocorrências raw_interpolation em qualquer posição. O raw_interpolation deve começar com um número de chaves abertas ({) igual ao número de cifrões no início do literal.
    • Se 'espaço em branco de recuo' não estiver vazio, um raw_interpolation não pode seguir imediatamente um new_line.
    • O raw_interpolation seguirá as regras normais especificadas em §12.8.3. Qualquer raw_interpolation deve terminar com o mesmo número de chaves fechadas (}) que os cifrões e chaves abertas.
    • Qualquer interpolation pode conter novas linhas dentro da mesma maneira que um interpolation em um verbatim_string_literal normal (@"").
    • Em seguida, termina com um new_line algum número (possivelmente zero) de whitespace e o mesmo número de aspas com que o literal começou.

O cálculo do valor da cadeia de caracteres interpolada segue as mesmas regras de um raw_string_literal normal, exceto atualizado para lidar com linhas contendo raw_interpolations. A construção do valor da cadeia de caracteres acontece da mesma maneira, apenas com os orifícios de interpolação substituídos por quaisquer valores que essas expressões produzam em tempo de execução. Se o interpolated_raw_string_literal é convertido em um FormattableString então os valores das interpolações são passados em sua respetiva ordem para a matriz arguments para FormattableString.Create. O resto do conteúdo do interpolated_raw_string_literal, após a remoção de do 'espaço de indentação' de todas as linhas, será usado para gerar a cadeia de caracteres format passada para FormattableString.Create, exceto com conteúdos de {N} devidamente numerados em cada local de ocorrência de raw_interpolation (ou {N,constant}, caso a sua interpolation esteja na forma expression ',' constant_expression).

Há uma ambiguidade na especificação acima. Especificamente quando uma seção de { no texto e { de uma interpolação estão adjacentes. Por exemplo:

var v1 = $$"""
         {{{order_number}}}
         """

Isto pode ser interpretado como: {{ {order_number } }} ou { {{order_number}} }. No entanto, como o primeiro é ilegal (nenhuma expressão C# poderia começar com {), seria inútil interpretá-lo dessa forma. Interpretamos desta última forma, onde as chaves mais internas { e } formam a interpolação, e quaisquer outras mais exteriores formam o texto. No futuro, isso pode ser um problema se a linguagem suportar quaisquer expressões que estejam cercadas por chaves. No entanto, nesse caso, a recomendação seria escrever um caso assim: {{({some_new_expression_form})}}. Aqui, parênteses ajudariam a designar a parte da expressão do resto da literal/interpolação. Isso já tem precedência com a forma como as expressões condicionais ternárias precisam ser encapsuladas para não entrar em conflito com o especificador de formatação/alinhamento de uma interpolação (por exemplo, {(x ? y : z)}).

Desvantagens

Literais de cadeia de caracteres brutos adicionam mais complexidade à linguagem. Já temos muitas formas literais de cadeia de caracteres para vários fins. "" strings, @"" strings e $"" strings já têm muito poder e flexibilidade. Mas todos eles carecem de uma maneira de fornecer conteúdo bruto que nunca precisa escapar.

As regras acima não suportam o caso de 4.a:

  1. ...
    • Opcionalmente, com complexidade extra, poderíamos refinar isso para afirmar que: Para todas as cadeias de caracteres que não contêm uma new_line (mas podem começar ou terminar com uma citação " caractere), deve ser possível representar o próprio literal da cadeia de caracteres em uma única linha.

Isso porque não temos como saber que uma citação inicial ou final (") deve pertencer ao conteúdo e não ao delimitador em si. Se este é um cenário importante que queremos apoiar, podemos adicionar uma construção de ''' paralela para acompanhar a forma """. Com essa construção paralela, uma única cadeia de caracteres de linha que começa e termina com " pode ser escrita facilmente como '''"This string starts and ends with quotes"''' juntamente com a construção paralela """'This string starts and ends with apostrophes'""". Isso também pode ser desejável para ajudar a separar visualmente os caracteres de aspas, o que pode ajudar ao incorporar idiomas que usam principalmente um caractere de aspas muito mais do que outro.

Alternativas

https://github.com/dotnet/csharplang/discussions/89 abrange muitas opções aqui. As alternativas são inúmeras, mas acho que se afastam demasiado no sentido da complexidade e da má ergonomia. Essa abordagem opta pela simplicidade, onde você apenas continua aumentando o comprimento da cotação inicial/final até que não haja preocupação com um conflito com o conteúdo da cadeia de caracteres. Ele também permite que o código que se escreve fique bem indentado, enquanto ainda produz um literal sem indentação, que é o que a maioria do código deseja.

Uma das variações potenciais mais interessantes, no entanto, é o uso de cercas ` (ou ```) para esses literais de cadeia brutos. Isto teria várias vantagens:

  1. Isso evitaria todos os problemas com sequências começando ou terminando com aspas.
  2. Seria semelhante ao markdown. Embora isso em si não seja potencialmente uma coisa boa, pois os usuários podem esperar uma interpretação de markdown.
  3. Um literal de cadeia de caracteres bruto só teria que começar e terminar com um único caractere na maioria dos casos, e só precisaria de vários no caso muito mais raro de conteúdo que contém back-ticks em si.
  4. Seria natural estender isso no futuro com ```xml, novamente semelhante ao markdown. Embora, é claro, isso também seja verdade para a forma """.

No geral, porém, o benefício líquido aqui parece pequeno. De acordo com a história do C#, acho que " deve continuar a ser o delimitador string literal, assim como é para @"" e $"".

Reuniões de design

Questões em aberto para discutir Problemas resolvidos:

  • [x] Devemos ter um formulário de linha única? Tecnicamente, poderíamos prescindir dele. Mas isso significaria que cadeias simples que não contivessem uma nova linha sempre levariam pelo menos três linhas. Considero que é muito pesado forçar construções de linha única a serem três linhas apenas para evitar o escape.

Decisão de design: Sim, teremos um formulário de linha única.

  • [x] Devemos exigir que a multilinha comece com uma nova linha? Penso que sim. Também nos dá a capacidade de apoiar coisas como """xml no futuro.

Decisão de design: Sim, vamos exigir que várias linhas comecem com uma nova linha

  • [x] O desdentamento automático deve ser feito? Penso que sim. Isso faz com que o código pareça muito mais agradável.

Decisão de projeto: Sim, o desdentamento automático será feito.

  • [x] Devemos restringir os espaços em branco comuns de misturar diferentes tipos de espaços em branco? Acho que não devemos. De fato, há uma estratégia de recuo comum chamada "guia para recuo, espaço para alinhamento". Seria muito natural usar isso para alinhar o delimitador final com o delimitador de início em um caso em que o delimitador de início não inicia em uma parada de tabulação.

Decisão de design: Não teremos quaisquer restrições na mistura de espaços em branco.

  • [x] Devemos usar algo mais para as cercas? ` corresponderia à sintaxe de markdown e significaria que não precisávamos sempre iniciar essas cadeias de caracteres com três aspas. Apenas um seria suficiente para o caso comum.

Decisão de design: Usaremos """

  • [x] Devemos ter um requisito de que o delimitador tenha mais aspas do que a sequência mais longa de aspas no valor da cadeia de caracteres? Tecnicamente não é obrigatório. Por exemplo:
var v = """
        contents"""""
        """

Esta é uma cadeia de caracteres com """ como delimitador. Vários membros da comunidade afirmaram que isso é confuso e devemos exigir em um caso como este que o delimitador sempre tenha mais caracteres. Isso seria, então:

var v = """"""
        contents"""""
        """"""

Decisão de design: Sim, o delimitador deve ser maior do que qualquer sequência de aspas na própria cadeia de caracteres.