c# - interpolacion - ¿Por qué la interpolación de cadenas no funciona con cadenas constantes?
string.format c# (4)
Hay una discusión en el proyecto Roslyn en Roslyn que finaliza la siguiente conclusión:
Lea el extracto:
No es un error, fue diseñado explícitamente para funcionar así. No te gusta no lo convierte en un error. String.Format no es necesario para concatenar cadenas, pero eso no es lo que estás haciendo. Los estás interpolando, y String.Format es necesario para eso, basado en la especificación y la implementación de cómo funciona la interpolación en C #.
Si desea concatenar cadenas, siga adelante y use la misma sintaxis que ha funcionado desde C # 1.0. Cambiar la implementación para comportarse de forma diferente según el uso produciría resultados inesperados:
const string FOO = "FOO";
const string BAR = "BAR";
string foobar = $"{FOO}{BAR}";
const string FOOBAR = $"{FOO}{BAR}"; // illegal today
Debug.Assert(foobar == FOOBAR); // might not always be true
Incluso la declaración:
private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
El compilador genera un error:
"The name ''WEB_API_ROOT'' does not exist in the current context".
La variable ''WEB_API_ROOT'' debe definirse en el mismo contexto
Entonces, para la pregunta de OP: ¿Por qué la interpolación de cadenas no funciona con cadenas constantes? Respuesta: Es por especificaciones C # 6. Para más detalles, lea .NET Compiler Platform ("Roslyn") - Interpolación de cadenas para C #
¿Por qué la interpolación de cadenas en c # no funciona con cadenas constantes? Por ejemplo:
private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
Desde mi punto de vista, todo se conoce en tiempo de compilación. ¿O es esa una característica que se añadirá más adelante?
Mensaje del compilador:
La expresión que se asigna a ''DynamicWebApiBuilder.WEB_API_PROJECT'' debe ser constante.
¡Muchas gracias!
Las cadenas interpoladas se convierten simplemente en llamadas a string.Format
. Así que tu línea anterior realmente lee
private const string WEB_API_PROJECT = string.Format("{0}project.json", WEB_API_ROOT);
Y esto no es una constante de tiempo de compilación, ya que se incluye una llamada de método.
Por otro lado, el compilador puede hacer la concatenación de cadenas (de literales simples y constantes de cadenas), por lo que esto funcionará:
private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = WEB_API_ROOT + "project.json";
o cambiar de const
a static readonly
:
private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
por lo que la cadena se inicializa (y se llama string.Format
) en el primer acceso a cualquier miembro del tipo declarante.
Una constante utilizada con string.Format
, por su naturaleza, estaría destinado a trabajar con un número específico de argumentos, cada uno de los cuales tiene un significado predeterminado.
En otras palabras, si creas esta constante:
const string FooFormat = "Foo named ''{0}'' was created on {1}.";
Luego, para usarlo, debes tener dos argumentos que probablemente se supone que son una string
y un DateTime
.
Entonces, incluso antes de la interpolación de cadenas, en cierto modo estábamos usando la constante como función. En otras palabras, en lugar de separar la constante, podría haber tenido más sentido ponerla en una función, así:
string FormatFooDescription(string fooName, DateTime createdDate) =>
string.Format("Foo named ''{0}'' was created on {1}.", fooName, createdDate);
Sigue siendo lo mismo, excepto que la constante (literal de cadena) ahora se encuentra con la función y los argumentos que la usan. Bien podrían estar juntos, porque la cadena de formato es inútil para cualquier otro propósito. Además, ahora puede ver la intención de los argumentos que se aplican a la cadena de formato.
Cuando lo vemos de esa manera, el uso similar de la interpolación de cuerdas se vuelve obvio:
string FormatFooDescription(string fooName, DateTime createdDate) =>
$"Foo named ''{fooName}'' was created on {createdDate}.";
¿Qué sucede si tenemos varias cadenas de formato y queremos elegir una en particular en el tiempo de ejecución?
En lugar de seleccionar qué cadena usar, podríamos seleccionar una función:
delegate string FooDescriptionFunction(string fooName, DateTime createdDate);
Entonces podríamos declarar implementaciones como esta:
static FooDescriptionFunction FormatFoo { get; } = (fooName, createdDate) =>
$"Foo named ''{fooName}'' was created on {createdDate}.";
O mejor aún:
delegate string FooDescriptionFunction(Foo foo);
static FooDescriptionFunction FormatFoo { get; } = (foo) =>
$"Foo named ''{foo.Name}'' was created on {foo.CreatedDate}.";
}
Una explicación adicional de por qué las expresiones de interpolación de cadenas no se consideran constantes es que no son constantes , incluso si todas sus entradas son constantes. En concreto, varían en función de la cultura actual. Intenta ejecutar el siguiente código:
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Console.WriteLine($"{3.14}");
CultureInfo.CurrentCulture = new CultureInfo("cs-CZ");
Console.WriteLine($"{3.14}");
Su salida es:
3.14
3,14
Tenga en cuenta que la salida es diferente, aunque la expresión de interpolación de cadenas sea la misma en ambos casos. Entonces, con la const string pi = $"{3.14}"
, no estaría claro qué código debería generar el compilador.