interpolacion entender ejemplos cadenas c# c#-6.0 string-interpolation

c# - entender - Problemas de interpolación de cadenas



string c# ejemplos (4)

El problema con esta linea

Assert.AreEqual(formatted, $"{{countdown|{date:o}}}");

es que tiene 3 comillas tipográficas después de la format string de format string de la variable que se va a escapar y comienza a escaparse de izquierda a derecha, por lo tanto trata las primeras 2 comillas tipográficas como parte de la cadena de formato y la tercera comilla tipográfica como la de cierre.

Por lo tanto, transforma o en o} y no puede interpolarlo.

Esto debería funcionar

Assert.AreEqual(formatted, $"{{countdown|{date:o}"+"}");

Observe que el simple $"{date}}}" (es decir, 3 rizos después de la variable sin una format string ) funciona porque reconoce que la primera cita es la de cierre, mientras que la interpretación del especificador de formato después de : rompe la correcta identificación de paréntesis de cierre.

Para demostrar que la cadena de formato se escapó como una cadena, considere que la siguiente

$"{date:/x6f}"

es tratado como

$"{date:o}"

Finalmente, es perfectamente posible que las comillas dobles escapadas sean parte de un formato de fecha personalizado, por lo que es absolutamente razonable el comportamiento del compilador. De nuevo, un ejemplo concreto

$"{date:MMM}}dd}}yyy}" // it''s a valid feb}09}2017

El análisis es un proceso formal basado en las reglas de expresión gramatical, no se puede hacer simplemente echándolo un vistazo.

Estoy tratando de descubrir por qué mi prueba de unidad falla (la tercera afirmación a continuación):

var date = new DateTime(2017, 1, 1, 1, 0, 0); var formatted = "{countdown|" + date.ToString("o") + "}"; //Works Assert.AreEqual(date.ToString("o"), $"{date:o}"); //Works Assert.AreEqual(formatted, $"{{countdown|{date.ToString("o")}}}"); //This one fails Assert.AreEqual(formatted, $"{{countdown|{date:o}}}");

AFAIK, esto debería funcionar correctamente, pero parece que no pasa el parámetro de formateo correctamente, sino que aparece como {countdown|o} del código. ¿Alguna idea de por qué esto está fallando?


El problema parece ser que para insertar un paréntesis mientras utiliza la interpolación de cadenas, debe escapar de él duplicándolo. Si agrega el paréntesis utilizado para la interpolación en sí, terminamos con un paréntesis triple como el que tiene en la línea que le da la excepción:

Assert.AreEqual(formatted, $"{{countdown|{date:o}}}");

Ahora, si observamos el " }}}" , podemos observar que el primer paréntesis encierra la interpolación de cadena, mientras que los dos últimos están destinados a ser tratados como un carácter de paréntesis de cadena escapada.

Sin embargo, el compilador trata a los dos primeros como el carácter de cadena escapada, por lo que está insertando una cadena entre los delimitadores de interpolación. Básicamente, el compilador está haciendo algo como esto:

string str = "a string"; $"{str''}''}"; //this would obviously generate a compile error which is bypassed by this bug

Puede resolver esto formateando la línea como tal:

Assert.AreEqual(formatted, $"{{countdown|{$"{date:o}"}}}");


Esta es la forma más fácil de hacer funcionar la afirmación ...

Assert.AreEqual(formatted, "{" + $"countdown|{date:o}" + "}");

Ene sta forma...

Assert.AreEqual(formatted, $"{{countdown|{date:o}}}");

Los primeros 2 cierres de cierre se interpretan como un corchete de cierre literal y el tercero como cierre de la expresión de formateo.

Esto no es tanto un error como una limitación de la gramática para cadenas interpoladas. El error, si lo hay, es que la salida del texto formateado debería ser probablemente "o}" en lugar de solo "o".

La razón por la que tenemos el operador "+ =" en lugar de "= +" en C, C # y C ++ es que en la forma = + no se puede decir en algunos casos si el "+" es parte del operador o unario " + ".


Este es un seguimiento de mi respuesta original en orden

para asegurarse de que este es el comportamiento previsto

En lo que respecta a una fuente oficial, deberíamos referirnos a las cadenas interpoladas de msdn.

La estructura de una cadena interpolada es

$ " <text> { <interpolation-expression> <optional-comma-field-width> <optional-colon-format> } <text> ... } "

y cada interpolación individual se define formalmente con una sintaxis

single-interpolation: interpolation-start interpolation-start : regular-string-literal interpolation-start: expression expression , expression

Lo que cuenta aquí es que

  1. el optional-colon-format se define como una sintaxis de regular-string-literal => es decir, puede contener una escape-sequence , de acuerdo con el paragraph 2.4.4.5 String literals de la especificación de lenguaje C # 5.0
  2. Puede usar una cadena interpolada en cualquier lugar donde pueda usar una string literal
  3. Para incluir un corchete ( { o } ) en una cadena interpolada use dos llaves, {{ o }} => es decir, el compilador escapa de dos llaves en el optional-colon-format
  4. el compilador escanea las expressions interpolación contenidas como texto equilibrado hasta que encuentra una coma, dos puntos o cierra llaves => es decir, un dos puntos rompe el texto equilibrado , así como una llave estrecha

Para que quede claro, esto explica la diferencia entre $"{{{date}}}" donde date es una expression y, por lo tanto, se tokeniza hasta la primera llave con respecto a $"{{{date:o}}}" donde date es otra vez una expression y ahora está tokenizado hasta los primeros dos puntos, después de lo cual comienza un literal de cadena regular y el compilador reanuda escapando dos llaves, etc.

También están las preguntas frecuentes sobre el formato de cadena de msdn, donde este caso fue tratado explícitamente.

int i = 42; string s = String.Format(“{{{0:N}}}”, i); //prints ‘{N}’

La pregunta es, ¿por qué falló este último intento? Hay dos cosas que necesita saber para comprender este resultado:

Cuando se proporciona un especificador de formato, el formato de cadena realiza estos pasos:

Determine si el especificador es más largo que un solo carácter: si es así, suponga que el especificador es un formato personalizado. Un formato personalizado utilizará reemplazos adecuados para su formato, pero si no sabe qué hacer con algún personaje, simplemente lo escribirá como un literal encontrado en el formato Determinar si el especificador de caracteres individuales es un especificador compatible (tal como ''N'' para formato de número). Si es así, formatee apropiadamente. Si no, lanza una ArgumnetException

Al intentar determinar si un bracket debe escaparse, las llaves se tratan simplemente en el orden en que se reciben. Por lo tanto, {{{ escapará de los dos primeros caracteres e imprimirá el literal { , y el tercer corchete comenzará la sección de formateo. Sobre esta base, en }}} los primeros dos corchetes se escaparán, por lo tanto, se escribirá un literal } en la cadena de formato, y luego se asumirá que el último corchete finaliza una sección de formateo. Con esta información, ahora puede descubrir lo que está ocurriendo en nuestra situación {{{0:N}}} . Los dos primeros corchetes se escapan, y luego tenemos una sección de formateo. Sin embargo, también escapamos del corchete de cierre, antes de cerrar la sección de formateo. Por lo tanto, nuestra sección de formateo se interpreta realmente como que contiene 0:N} . Ahora, el formateador mira el especificador de formato y ve N} para el especificador. Por lo tanto, interpreta esto como un formato personalizado, y dado que ni N ni} significan nada para un formato numérico personalizado, estos caracteres simplemente se escriben, en lugar del valor de la variable a la que se hace referencia.