Compartir a través de


Literal de cadena sin formato

Nota:

Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos y se incorporan en la especificación ECMA actual.

Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.

Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C#, en el artículo sobre especificaciones.

Problema planteado por experto: https://github.com/dotnet/csharplang/issues/8647

Resumen

Permite una nueva forma de literal de cadena que comienza con un mínimo de tres caracteres de """ (pero no como máximo), seguido opcionalmente de una new_line, el contenido de la cadena y, a continuación, termina con el mismo número de comillas con las que se inició el literal. Por ejemplo:

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

Como el contenido anidado podría querer usar """, los delimitadores iniciales o finales pueden ser más largos como el siguiente:

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

Para que el texto sea fácil de leer y permita la sangría que les gusta a los desarrolladores en el código, estos literales de cadena quitarán de forma natural la sangría especificada en la última línea al generar el valor literal final. Por ejemplo, un literal con el formato:

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

Tendrá los contenidos:

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

Esto permite que el código tenga un aspecto natural, a la vez que genera literales deseados y evita los costes en tiempo de ejecución si se necesitan usar rutinas especializadas de manipulación de cadenas.

Si no se desea el comportamiento de sangría, también es trivial deshabilitarlo de la siguiente manera:

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

También se admite la forma de una sola línea. Comienza con un mínimo de tres caracteres """ (pero no es el máximo), el contenido de la cadena (que no puede incluir ningún carácter new_line) y finalmente termina con el mismo número de comillas con las que comenzó el literal. Por ejemplo:

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

También se admiten cadenas sin procesar interpoladas. En este caso, la cadena especifica el número de llaves necesarias para iniciar una interpolación (determinada por el número de signos de dólar presentes al principio del literal). Cualquier secuencia de llaves que tenga menos llaves que esa simplemente se trata como contenido. Por ejemplo:

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

Motivación

C# carece de un método general de crear literales de cadena simples que puedan incluir eficazmente cualquier texto arbitrario. En la actualidad, todos las formas de literales de cadena de C# necesitan alguna forma de escape en caso de que el contenido use algún carácter especial (siempre si se usa un delimitador). Esto evita que salgan con facilidad literales que contengan otros lenguajes (por ejemplo, un literal de XML, HTML o JSON).

Hoy en día, todos los métodos actuales para formar estos literales en C# siempre obligan al usuario a escapar manualmente el contenido. La edición en ese punto puede ser muy molesta, ya que el escape no se puede evitar y debe tratarse siempre que surja en los contenidos. Esto es especialmente molesto para las expresiones regulares, especialmente cuando contienen comillas o barras diagonales inversas. Incluso con una cadena textual (@""), las comillas se deben escapar, lo que conduce a una combinación de C# y expresión regular intercalada. { y } son igualmente frustrantes en cadenas interpoladas ($"").

El argumento del problema es que todas nuestras cadenas tienen un delimitador de inicio y de finalización fijo. Siempre que sea así, tiene que haber un mecanismo de escape, ya que es posible que el contenido de la cadena tenga que especificar ese delimitador final en el contenido. Esto es especialmente problemático, ya que el delimitador " es muy común en muchos lenguajes.

Para resolver esto, esta propuesta permite delimitadores de inicio y finalización flexibles para que siempre se puedan realizar de forma que no entren en conflicto con el contenido de la cadena.

Objetivos

  1. Proporcione un mecanismo que permita que el usuario proporcione todos los valores de cadena sin necesidad de ningún tipo de secuencia de escape. Dado que todas las cadenas se deben representar sin secuencias de escape, debe ser posible en todo momento que el usuario indique los delimitadores siempre que se confirme que no entren en conflicto con ningún contenido de texto.
  2. Admite interpolaciones de la misma manera. Según lo anterior, como todas las cadenas se deben representar sin secuencias de escape, debe ser posible en todo momento que el usuario indique un delimitador interpolation, siempre que se confirme que no entre en conflicto con ningún contenido de texto. Es importante que los lenguajes que usan nuestros caracteres delimitadores de interpolación ({ y }) sean de primera clase y no resulten difíciles de usar.
  3. Los literales de cadena multilínea deben tener un aspecto agradable en el código y no deben hacer que la sangría dentro de la unidad de compilación tenga un aspecto extraño. Es importante que los valores literales que no tienen sangría no se vean forzados a ocupar la primera columna del archivo, ya que esto puede interrumpir el flujo del código y se verán desalineados respecto al resto del código circundante.
    • Este comportamiento debe ser fácil de modificar manteniendo los literales del código claros y fáciles de leer.
  4. Para todas las cadenas que no contengan por sí mismas una new_line o que no comiencen o terminen con un carácter de comillas ("), debe ser posible representar el literal de cadena en una sola línea.
    • Opcionalmente, con complejidad adicional, podríamos ajustar esto para indicar que: para todas las cadenas que no contienen una new_line (pero que pueden comenzar o terminar con el carácter de comilla "), debería ser posible representar el literal de la cadena en sí mismo en una sola línea. Para obtener más información, consulte la propuesta más detallada en la sección Drawbacks.

Diseño detallado (caso de no interpolación)

Agregaremos una nueva producción de string_literal con el siguiente formato:

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

El delimitador final de un raw_string_literal debe coincidir con el delimitador inicial. Por lo tanto, si el delimitador inicial es """"", el delimitador final debe ser así también.

La gramática anterior de un raw_string_literal debe interpretarse de las siguientes formas:

  1. Empieza al menos con tres comillas (pero sin tope máximo entre comillas).
  2. A continuación, continúa con el contenido en la misma línea que las comillas iniciales. Este contenido en la misma línea puede estar en blanco o no. "en blanco" ("blank") es sinónimo de "espacio en blanco".
  3. Si el contenido de esa misma línea no está en blanco, no puede seguir ningún contenido adicional. En otras palabras, el literal debe terminar con el mismo número de comillas en esa misma línea.
  4. Si el contenido de la misma línea está en blanco, el literal puede continuar con una new_line y algún número de líneas de contenido posteriores y new_lines.
    • Una línea de contenido es cualquier texto excepto un new_line.
    • A continuación, termina con una new_line seguida de un número (posiblemente cero) de whitespace y el mismo número de comillas con las que se inició el literal.

Valor de literal de cadena sin formato

Las partes entre el raw_string_literal_delimiter inicial y final sirven para formar el valor del raw_string_literal de la siguiente manera:

  • En el caso de single_line_raw_string_literal, el valor del literal será exactamente el contenido entre el raw_string_literal_delimiter inicial y final.
  • En el caso de multi_line_raw_string_literal, el whitespace* new_line inicial y el new_line whitespace* final no forman parte del valor de la cadena. Sin embargo, la parte final de whitespace* precedente al terminal raw_string_literal_delimiter se considera el "espacio en blanco de sangría" y afectará a cómo se interpretan las otras líneas.
  • Para obtener el valor final, se recorre la secuencia de (raw_content | new_line)* y se realiza lo siguiente:
    • Si es un new_line, el contenido del new_line se agrega al valor de cadena final.
    • Si no es un raw_content "en blanco" (es decir, not_new_line+ incluirá un carácter que no sea whitespace):
      • el "espacio en blanco de sangría" debe ser un prefijo del raw_content. De lo contrario, es un error.
      • el "espacio en blanco de sangría" se elimina del inicio de raw_content y el resto se agrega al valor final de la cadena.
    • Si es un raw_content "en blanco" (es decir, not_new_line+ será completamente un whitespace):
      • El "espacio en blanco de sangría" debe ser un prefijo de raw_content o raw_content debe ser un prefijo del "espacio en blanco de sangría". De lo contrario, es un error.
      • la mayor parte del "espacio en blanco de sangría" se quita del principio de raw_content y cualquier resto se agrega al valor de cadena final.

Aclaraciones:

  1. Un single_line_raw_string_literal no es capaz de representar una cadena con un valor new_line en él. Un single_line_raw_string_literal no participa en el recorte de "espacios en blanco de sangría". Su valor son siempre los caracteres exactos entre los delimitadores inicial y final.

  2. Dado que un multi_line_raw_string_literal omite el new_line final de la última línea de contenido, lo siguiente representa una cadena sin new_line inicial y sin new_line final.

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

Esto mantiene la simetría con la forma en que se omite la new_line inicial y también proporciona una manera uniforme de asegurarse de que siempre se pueda ajustar el "espacio en blanco de sangría". Para representar una cadena con un new_line fina, se debe añadir una línea adicional de la siguiente manera:

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

         """;
  1. Un single_line_raw_string_literal no puede representar un valor de cadena que comience o termine con una cita ("), aunque se proporciona una mejora a esta propuesta en la sección Drawbacks que muestra cómo se puede admitir.

  2. Un multi_line_raw_string_literal empieza por whitespace* new_line después del raw_string_literal_delimiter inicial. Este contenido después del delimitador se omite por completo y no se usa de ninguna manera al determinar el valor de la cadena. Esto permite que un mecanismo especifique un raw_string_literal cuyo contenido comienza con un carácter ". Por ejemplo:

var v1 = """
         "The content of this string starts with a quote
         """;
  1. Un raw_string_literal también puede representar el contenido que termina con una comilla ("). Esto se admite porque el delimitador de terminación debe estar en su propia línea. Por ejemplo:
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. El requisito de que un raw_content 'en blanco' sea un prefijo del 'espacio en blanco de sangría' o de que el 'espacio en blanco de sangría' sea un prefijo de este, ayuda a asegurar que no se produzcan escenarios confusos con espacios en blanco mixtos, especialmente porque quedaría poco claro qué debería suceder con esa línea. Por ejemplo, lo siguiente no está permitido:
var v1 = """
         Start
<tab>
         End
         """;
  1. Aquí el "espacio en blanco de sangría" consiste en nueve caracteres de espacio, pero el raw_content "en blanco" no comienza con dicho prefijo. No hay ninguna respuesta clara en cuanto a cómo se debe tratar la línea <tab>. ¿Se debería omitir? ¿Debería ser igual que .........<tab>? Por ello, hacer que sea algo no permitido parece lo más evidente para evitar confusiones.

  2. Sin embargo, las situaciones siguientes están permitidas y representan la misma cadena:

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

En ambos casos, el "espacio en blanco de sangría" será de nueve espacios. Y en ambos casos, quitaremos tanto de ese prefijo como sea posible, haciendo que el raw_content "en blanco" en cada caso esté vacío (sin contar cada new_line). Esto permite a los usuarios no tener que ver y potencialmente preocuparse por el espacio en blanco en estas líneas cuando copian, pegan o editan estas líneas.

  1. Sin embargo, fijémonos en el caso siguiente:
var v1 = """
         Start
<ten spaces>
         End
         """;

El "espacio en blanco de sangría" seguirá siendo de nueve espacios. Sin embargo, quitaremos la mayor cantidad posible de los "espacios en blanco de sangría" y el raw_content "en blanco" aportará un único espacio en el contenido final. Esto permite casos en los que el contenido necesita espacios en blanco en estas líneas que se deben conservar.

  1. Técnicamente, lo siguiente no está permitido:
var v1 = """
         """;

Esto se debe a que el inicio de la cadena sin formato debe tener una new_line (que la tiene) pero el final debe tener también una new_line (que no la tiene). El raw_string_literal mínimo legal es:

var v1 = """

         """;

Sin embargo, esta cadena es decididamente poco interesante, ya que es equivalente a "".

Ejemplos de sangría

El algoritmo de "espacio en blanco de sangría" se puede visualizar en varias entradas de la siguiente manera. En los ejemplos siguientes se usa el carácter de barra vertical | para ilustrar la primera columna de la cadena sin formato resultante:

Ejemplo 1: Caso estándar

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

se interpreta como

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

Ejemplo 2: Delimitador final en la misma línea que el contenido.

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

Esto no está permitido. La última línea de contenido debe terminar con un new_line.

Ejemplo 3: Delimitador final antes del delimitador inicial

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

se interpreta como

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

Ejemplo 4: Delimitador final después del delimitador inicial

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

Esto no está permitido. Las líneas de contenido deben comenzar con "el espacio en blanco de sangría".

Ejemplo 5: Línea vacía en blanco

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

          </element>
          """;

se interpreta como

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

Ejemplo 6: Línea en blanco con menos espacios en blanco que el prefijo (los puntos representan espacios)

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

se interpreta como

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

Ejemplo 7: Línea en blanco con más espacios en blanco que el prefijo (los puntos representan espacios)

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

se interpreta como

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

Diseño detallado (caso de interpolación)

Las interpolaciones en cadenas interpoladas normales (por ejemplo, $"...") se admiten hoy en día mediante el uso del carácter de { para iniciar una interpolation y el uso de una secuencia de escape {{ para insertar un carácter de llave de apertura real. El uso de este mismo mecanismo contravendría los objetivos "1" y "2" de esta propuesta. Los lenguajes que tienen { como carácter principal (por ejemplo, JavaScript, JSON, Regex e incluso C# incrustado) ahora necesitarían escaparse, anulando el propósito de los literales de cadena sin formato.

Para admitir interpolaciones, las introducimos de una manera diferente a las normales $" cadenas interpoladas. En concreto, un interpolated_raw_string_literal comenzará con un cierto número de caracteres $. El número de estos indica cuántos caracteres { (y }) son necesarios en el contenido del literal para delimitar el interpolation. Es importante indicar que sigue sin haber ningún mecanismo de escape para llaves. En su lugar, al igual que con las comillas ("), el propio literal siempre puede asegurarse de que indica los delimitadores para las interpolaciones que seguramente no vayan a entrar en conflicto con ningún contenido restante de la cadena. Por ejemplo, un literal de JSON que incluye huecos de interpolación se puede escribir de la siguiente manera:

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

Aquí, {{...}} coincide con la cantidad requerida de dos llaves especificadas por el delimitador con prefijo $$. En el caso de una sola $, esto significa que la interpolación se especifica de la misma manera que {...}, tal como se hace en los literales de cadena interpolados normales. Es importante indicar que esto significa que un literal interpolado con caracteres N$ puede tener una secuencia de llaves 2*N-1 (del mismo tipo en una fila). Las últimas llaves N iniciarán (o finalizarán) una interpolación y las llaves N-1 restantes solo serán contenido. Por ejemplo:

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

En este caso, las dos llaves internas {{ y }} pertenecen a la interpolación, y las llaves singulares externas son solo contenido. Por lo tanto, la cadena anterior es equivalente al contenido X{2}Z. Tener 2*N (o más) llaves siempre es un error. Para tener secuencias más largas de llaves como contenido, la cantidad de caracteres $ debe aumentarse en consecuencia.

Los literales de cadena sin procesar interpolados se definen de esta forma:

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

Lo anterior es similar a la definición de raw_string_literal, pero con algunas diferencias importantes. Un interpolated_raw_string_literal debe interpretarse como:

  1. Empieza al menos por el signo del dólar (pero sin tope máximo) y luego tres comillas (también sin tope máximo).
  2. A continuación, continúa con el contenido en la misma línea que las comillas iniciales. Este contenido de la misma línea puede estar en blanco o no. "en blanco" ("blank") es sinónimo de "espacio en blanco".
  3. Si el contenido de esa misma línea no está en blanco, no puede seguir contenido adicional. En otras palabras, el literal debe terminar con el mismo número de comillas en esa misma línea.
  4. Si el contenido de la misma línea está en blanco, el literal puede continuar con una new_line y algún número de líneas de contenido posteriores y new_lines.
    • Una línea de contenido es cualquier texto excepto un new_line.
    • Una línea de contenido puede contener múltiples ocurrencias de raw_interpolation en cualquier posición. La raw_interpolation debe comenzar con un número igual de llaves de apertura ({) como el número de signos de dólar al principio del literal.
    • Si el 'espacio en blanco de sangría' no está vacío, una raw_interpolation no puede seguir inmediatamente a una new_line.
    • El raw_interpolation seguirá las reglas normales indicadas en §12.8.3. Cualquier raw_interpolation debe terminar con el mismo número de llaves de cierre (}) que los signos de dólar y las llaves de apertura.
    • Cualquier interpolation puede incluir líneas nuevas dentro de la misma manera que un interpolation en un verbatim_string_literal normal (@"").
    • A continuación, termina con una new_line seguida de un número (posiblemente cero) de whitespace y el mismo número de comillas con las que se inició el literal.

El cálculo del valor de cadena interpolada sigue las mismas reglas que un raw_string_literal normal, excepto que se actualiza para controlar las líneas que contienen raw_interpolations. La creación del valor de cadena se produce de la misma manera, solo con huecos de interpolación reemplazados por los valores que producen esas expresiones en tiempo de ejecución. Si el interpolated_raw_string_literal se convierte en un FormattableString, los valores de las interpolaciones se pasan en su orden respectivo a la matriz de arguments en FormattableString.Create. El resto del contenido del interpolated_raw_string_literaldespués de que el "espacio en blanco de sangría" se haya quitado de todas las líneas se usará para generar la cadena format pasada a FormattableString.Create, excepto con el contenido {N} numerado adecuadamente en cada ubicación donde se produjo una raw_interpolation (o {N,constant} en caso de que su interpolation tenga el formato expression ',' constant_expression).

Existe una ambigüedad en la especificación anterior. En concreto, cuando una sección de { en el texto y { de una interpolación se juntan. Por ejemplo:

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

Esto puede interpretarse como: {{ {order_number } }} o { {{order_number}} }. Sin embargo, como el anterior no está permitido (ninguna expresión de C# podría empezar por {), por lo que sería inútil hacer la interpretación de esa manera. Por lo tanto, interpretamos de esta última manera, donde las llaves { y } más internas forman la interpolación, y las llaves más externas forman el texto. En el futuro, esto podría ser un problema si el lenguaje de programación admite alguna vez expresiones que están rodeadas por llaves. Sin embargo, en ese caso, lo recomendable sería escribirlo de la siguiente manera: {{({some_new_expression_form})}}. Aquí, los paréntesis ayudarán a distinguir la parte de expresión del resto del literal/interpolación. Esto ya tiene prioridad con la forma en que las expresiones condicionales ternarias deben encapsularse para no entrar en conflicto con el especificador de formato o alineación de una interpolación (por ejemplo, {(x ? y : z)}).

Inconvenientes

Los literales de cadenas sin formato añaden más complejidad al lenguaje. Ya tenemos muchos formatos literales de cadena ya con numerosos fines. Cadenas "", @"" y $"" ya tienen mucha potencia y flexibilidad. Pero todas carecen de una manera de proporcionar contenido sin procesar que nunca se necesiten escapar.

Las reglas anteriores no admiten lo visto en el apartado 4.a:

  1. ...
    • Opcionalmente, con complejidad adicional, podríamos ajustar esto para indicar que: para todas las cadenas que no contienen una new_line (pero que pueden comenzar o terminar con el carácter de comilla "), debería ser posible representar el literal de la cadena en sí mismo en una sola línea.

Esto se debe a que no tenemos los medios para saber que una comilla inicial o final (") debe pertenecer al contenido y no al propio delimitador. Sin embargo, si se trata de un escenario importante que queremos admitir, podemos agregar una construcción de ''' paralela para continuar con el formato """. Con esa construcción paralela, se puede escribir fácilmente una cadena de una sola línea que empieza y termina con " como '''"This string starts and ends with quotes"''' junto con la construcción paralela """'This string starts and ends with apostrophes'""". También puede ser deseable admitir esto para ayudar a separar visualmente los caracteres de cita, lo que puede ser útil al incrustar idiomas que usan principalmente un carácter de cita mucho más que el otro.

Alternativas

Se abordan muchas cuestiones en https://github.com/dotnet/csharplang/discussions/89. Las alternativas son numerosas, pero siento que se alejan demasiado hacia la complejidad y la mala ergonomía. Este enfoque sigue la simplicidad, donde simplemente continúas aumentando la longitud de las comillas de apertura y cierre hasta que no haya riesgo de conflicto con el contenido de la cadena. También permite que el código que escriba tenga una buena sangría, al tiempo que produce un literal sin sangría, lo que se prefiere para la mayoría del código.

Sin embargo, una de las variaciones potenciales más interesantes es el uso de delimitadores ` (o ```) para estos literales de cadena sin formato. Esto tendría varias ventajas:

  1. Evitaría todos los problemas con las cadenas que comienzan o terminan con comillas.
  2. Se parecería a Markdown. Aunque eso en sí mismo no es potencialmente algo bueno, ya que los usuarios podrían esperar una interpretación de Markdown.
  3. Un literal de cadena sin formato solo tendría que empezar y terminar con un solo carácter en la mayoría de los casos, y solo necesitaría varios en el caso mucho más raro de contenidos que incluyan comillas inversas.
  4. Sería natural ampliar esto en el futuro con ```xml, de nuevo similar a Markdown. Sin embargo, por supuesto, eso también es cierto para la forma """.

Sin embargo, el beneficio neto aquí sería menor. De acuerdo con la historia de C#, opino que " debe seguir siendo el delimitador de string literal, tal como lo es para @"" y $"".

Reuniones de diseño

Problemas sin resolver para comentar Problemas resueltos:

  • [x] ¿Se debería tener un formato de una sola línea? Técnicamente se podría hacer sin eso. Pero significaría que las cadenas simples que no incluyen una nueva línea siempre ocuparían al menos tres líneas. Es muy pesado forzar construcciones de una sola línea a ser de tres líneas solo para evitar escapes.

Decisión de diseño: Sí, tendremos un formato de una sola línea.

  • [x] ¿deberíamos exigir que una multilínea empiece con una nueva línea? Creo que se debería usar. También nos da la capacidad de admitir cosas como """xml en el futuro.

Decisión de diseño: Sí, se requerirá que una multilínea empiece con una nueva línea.

  • [x] ¿debería realizarse la anulación de sangría automática? Creo que se debería usar. Esto hace que el código luzca mejor.

Decisión de diseño: Sí, se aplicaría la anulación de sangría automática.

  • [x] ¿deberíamos restringir que el espacio en blanco común mezcle tipos de espacios en blanco? No creo que deberíamos hacer esto. De hecho, hay una estrategia de sangría común denominada "tabulación para sangría, espacio para la alineación". Sería muy natural usarlo para alinear el delimitador final con el delimitador inicial en un caso en el que el delimitador inicial no se inicie en una tabulación.

Decisión de diseño: No habrá restricciones en la combinación de espacios en blanco.

  • [x] ¿Se debería usar otra cosa para los límites? ` sería igual que la sintaxis de markdown y supondría que no sería necesario empezar siempre estas cadenas con tres comillas. Bastaría uno para el caso habitual.

Decisión de diseño: Usaremos """

  • [x] ¿Debería haber un requisito para que el delimitador tenga más comillas que la secuencia de comillas más largas del valor de cadena? Técnicamente, no es necesario. por ejemplo:
var v = """
        contents"""""
        """

Se trata de una cadena con """ como delimitador. Varios miembros de la comunidad han afirmado que esto lleva a confusión y en un caso así el delimitador debería estar obligado a tener siempre más caracteres. Esto daría el siguiente resultado:

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

Decisión de diseño: Sí, el delimitador debe ser más largo que cualquier secuencia de comillas en la propia cadena.