C# Language Interpolación de cuerdas


Ejemplo

La interpolación de cadenas permite al desarrollador combinar variables y texto para formar una cadena.


Ejemplo básico

Se crean dos variables int : foo y bar .

int foo = 34;
int bar = 42;

string resultString = $"The foo is {foo}, and the bar is {bar}.";

Console.WriteLine(resultString);

Salida :

El foo es 34, y la barra es 42.

Ver demostración

Todavía se pueden usar llaves dentro de las cuerdas, como esto:

var foo = 34;
var bar = 42;

// String interpolation notation (new style)
Console.WriteLine($"The foo is {{foo}}, and the bar is {{bar}}.");

Esto produce el siguiente resultado:

El foo es {foo}, y la barra es {bar}.


Usando la interpolación con literales de cadena textual.

Usar @ antes de la cadena hará que la cadena sea interpretada textualmente. Entonces, por ejemplo, los caracteres Unicode o los saltos de línea se mantendrán exactamente como se han escrito. Sin embargo, esto no afectará las expresiones en una cadena interpolada como se muestra en el siguiente ejemplo:

Console.WriteLine($@"In case it wasn't clear:
\u00B9
The foo
is {foo},
and the bar
is {bar}.");
Salida:

En caso de que no estuviera claro:
\ u00B9
El foo
es 34,
y el bar
es 42

Ver demostración


Expresiones

Con la interpolación de cadenas, las expresiones entre llaves {} también se pueden evaluar. El resultado se insertará en la ubicación correspondiente dentro de la cadena. Por ejemplo, para calcular el máximo de foo y bar e insertarlo, use Math.Max dentro de las llaves:

Console.WriteLine($"And the greater one is: { Math.Max(foo, bar) }");

Salida:

Y el mayor es: 42.

Nota: Cualquier espacio en blanco inicial o final (incluidos el espacio, la pestaña y CRLF / nueva línea) entre la llave y la expresión se ignoran por completo y no se incluyen en la salida

Ver demostración

Como otro ejemplo, las variables se pueden formatear como una moneda:

Console.WriteLine($"Foo formatted as a currency to 4 decimal places: {foo:c4}");

Salida:

Foo formateado como una moneda con 4 decimales: $ 34.0000

Ver demostración

O se pueden formatear como fechas:

Console.WriteLine($"Today is: {DateTime.Today:dddd, MMMM dd - yyyy}");

Salida:

Hoy es: lunes 20 de julio de 2015.

Ver demostración

Las declaraciones con un operador condicional (ternario) también se pueden evaluar dentro de la interpolación. Sin embargo, estos deben estar envueltos entre paréntesis, ya que los dos puntos se utilizan para indicar el formato como se muestra arriba:

Console.WriteLine($"{(foo > bar ? "Foo is larger than bar!" : "Bar is larger than foo!")}");

Salida:

Bar es más grande que foo!

Ver demostración

Se pueden mezclar expresiones condicionales y especificadores de formato:

Console.WriteLine($"Environment: {(Environment.Is64BitProcess ? 64 : 32):00'-bit'} process");

Salida:

Medio ambiente: proceso de 32 bits


Secuencias de escape

Los caracteres de barra diagonal inversa ( \ ) y comillas ( " ) funcionan exactamente igual en las cadenas interpoladas que en las cadenas no interpoladas, tanto para literales de cadena textuales como no literales:

Console.WriteLine($"Foo is: {foo}. In a non-verbatim string, we need to escape \" and \\ with backslashes.");
Console.WriteLine($@"Foo is: {foo}. In a verbatim string, we need to escape "" with an extra quote, but we don't need to escape \");

Salida:

Foo es 34. En una cadena no verbal, necesitamos escapar "y \ con barras invertidas.
Foo tiene 34. En una cadena textual, necesitamos escapar "con una cita extra, pero no necesitamos escapar \

Para incluir un corchete { o } en una cadena interpolada, use dos corchetes {{ o }} :

$"{{foo}} is: {foo}"

Salida:

{foo} es: 34

Ver demostración


Tipo FormattableString

El tipo de expresión de interpolación de cadena $"..." no siempre es una cadena simple. El compilador decide qué tipo asignar según el contexto:

string s = $"hello, {name}";
System.FormattableString s = $"Hello, {name}";
System.IFormattable s = $"Hello, {name}";

Este es también el orden de preferencia de tipo cuando el compilador necesita elegir a qué método sobrecargado se llamará.

Un nuevo tipo , System.FormattableString , representa una cadena de formato compuesto, junto con los argumentos a formatear. Use esto para escribir aplicaciones que manejen los argumentos de interpolación específicamente:

public void AddLogItem(FormattableString formattableString)
{
    foreach (var arg in formattableString.GetArguments())
    {
        // do something to interpolation argument 'arg'
    }

    // use the standard interpolation and the current culture info
    // to get an ordinary String:
    var formatted = formattableString.ToString();

    // ...
}

Llame al método anterior con:

AddLogItem($"The foo is {foo}, and the bar is {bar}.");
Por ejemplo, uno podría elegir no incurrir en el costo de rendimiento de formatear la cadena si el nivel de registro ya iba a filtrar el elemento de registro.

Conversiones implícitas

Hay conversiones de tipo implícitas de una cadena interpolada:

var s = $"Foo: {foo}";
System.IFormattable s = $"Foo: {foo}";
También puede producir una variable IFormattable que le permita convertir la cadena con un contexto invariante:
var s = $"Bar: {bar}";
System.FormattableString s = $"Bar: {bar}";

Métodos de cultivo actuales e invariantes

Si el análisis de código está activado, todas las cadenas interpoladas producirán una advertencia CA1305 (Especifique IFormatProvider ). Se puede utilizar un método estático para aplicar la cultura actual.

public static class Culture
{
    public static string Current(FormattableString formattableString)
    {
        return formattableString?.ToString(CultureInfo.CurrentCulture);
    }
    public static string Invariant(FormattableString formattableString)
    {
        return formattableString?.ToString(CultureInfo.InvariantCulture);
    }
}

Luego, para producir una cadena correcta para la cultura actual, solo use la expresión:

Culture.Current($"interpolated {typeof(string).Name} string.")
Culture.Invariant($"interpolated {typeof(string).Name} string.")
Nota : Current e Invariant no se pueden crear como métodos de extensión porque, de forma predeterminada, el compilador asigna el tipo String a la expresión de cadena interpolada, lo que hace que el siguiente código no se compile:

$"interpolated {typeof(string).Name} string.".Current();

FormattableString clase FormattableString ya contiene el método Invariant() , por lo que la forma más sencilla de cambiar a una cultura invariante es using static :

using static System.FormattableString;

string invariant = Invariant($"Now = {DateTime.Now}"); string current = $"Now = {DateTime.Now}";


Entre bastidores

Las cadenas interpoladas son solo un azúcar sintáctico para String.Format() . El compilador ( Roslyn ) lo convertirá en un String.Format detrás de escena:

var text = $"Hello {name + lastName}";

Lo anterior se convertirá en algo como esto:

string text = string.Format("Hello {0}", new object[] {
    name + lastName
});

Interpolación de cuerdas y Linq

Es posible usar cadenas interpoladas en las declaraciones de Linq para aumentar aún más la legibilidad.

var fooBar = (from DataRow x in fooBarTable.Rows
          select string.Format("{0}{1}", x["foo"], x["bar"])).ToList();

Puede ser reescrito como:

var fooBar = (from DataRow x in fooBarTable.Rows
          select $"{x["foo"]}{x["bar"]}").ToList();

Cuerdas interpoladas reutilizables

Con string.Format , puede crear cadenas de formato reutilizables:

public const string ErrorFormat = "Exception caught:\r\n{0}";

// ...

Logger.Log(string.Format(ErrorFormat, ex));

Sin embargo, las cadenas interpoladas no se compilarán con los marcadores de posición que se refieren a variables no existentes. Lo siguiente no se compilará:

public const string ErrorFormat = $"Exception caught:\r\n{error}";
// CS0103: The name 'error' does not exist in the current context

En su lugar, cree un Func<> que consume variables y devuelve una String :

public static Func<Exception, string> FormatError =
    error => $"Exception caught:\r\n{error}";

// ...

Logger.Log(FormatError(ex));

Interpolación de cuerdas y localización.

Si está localizando su aplicación, puede preguntarse si es posible utilizar la interpolación de cadenas junto con la localización. De hecho, sería bueno tener la posibilidad de almacenar en archivos de recursos String s como:

"My name is {name} {middlename} {surname}"
En lugar de mucho menos legible:

"My name is {0} {1} {2}"

String proceso de interpolación de String se produce en tiempo de compilación , a diferencia del formato de cadena con cadena. string.Format que ocurre en tiempo de ejecución . Las expresiones en una cadena interpolada deben hacer referencia a los nombres en el contexto actual y deben almacenarse en archivos de recursos. Eso significa que si quieres usar la localización tienes que hacerlo como:

var FirstName = "John";

// method using different resource file "strings"
// for French ("strings.fr.resx"), German ("strings.de.resx"), 
// and English ("strings.en.resx")
void ShowMyNameLocalized(string name, string middlename = "", string surname = "")
{
    // get localized string
    var localizedMyNameIs = Properties.strings.Hello;
    // insert spaces where necessary
    name = (string.IsNullOrWhiteSpace(name) ? "" : name + " ");
    middlename = (string.IsNullOrWhiteSpace(middlename) ? "" : middlename + " ");
    surname = (string.IsNullOrWhiteSpace(surname) ? "" : surname + " ");
    // display it
    Console.WriteLine($"{localizedMyNameIs} {name}{middlename}{surname}".Trim());
}

// switch to French and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
ShowMyNameLocalized(FirstName);

// switch to German and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("de-DE");
ShowMyNameLocalized(FirstName);

// switch to US English and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
ShowMyNameLocalized(FirstName);

Si las cadenas de recursos para los idiomas utilizados anteriormente se almacenan correctamente en los archivos de recursos individuales, debe obtener el siguiente resultado:

Bonjour, mon nom est John
Hola, mein Nombre ist John
Hola, mi nombre es John

Tenga en cuenta que esto implica que el nombre sigue la cadena localizada en todos los idiomas. Si ese no es el caso, debe agregar marcadores de posición a las cadenas de recursos y modificar la función anterior o debe consultar la información de cultura en la función y proporcionar una declaración de cambio de caso que contenga los diferentes casos. Para obtener más detalles sobre los archivos de recursos, consulte Cómo usar la localización en C # .

Es una buena práctica usar un idioma alternativo predeterminado que la mayoría de la gente entenderá, en caso de que no haya una traducción disponible. Sugiero utilizar el inglés como idioma alternativo predeterminado.

Interpolación recursiva

Aunque no es muy útil, se permite usar una string interpolada recursivamente dentro de los corchetes de otra persona:

Console.WriteLine($"String has {$"My class is called {nameof(MyClass)}.".Length} chars:");
Console.WriteLine($"My class is called {nameof(MyClass)}.");

Salida:

La cadena tiene 27 caracteres:

Mi clase se llama MyClass.