number interpolacion cadenas before antes c# string type-inference method-overloading string-interpolation

c# - interpolacion - string interpolation javascript



¿Cuál es el tipo original de cadena interpolada? (3)

Los docs MSDN contienen la sección sobre conversiones implícitas:

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

De la primera cadena se deduce que el tipo original de cadena interpolada es la string . Ok, puedo entenderlo, pero entonces ... Me doy cuenta de que la cadena no implementa IFormattable . Así que parece algo mágico del compilador similar a lo que hace con las lambdas.

Ahora adivina la salida de este código:

void Main() { PrintMe("Hello World"); PrintMe($"{ "Hello World"}"); } void PrintMe(object message) { Console.WriteLine("I am a " + message.GetType().FullName); } //void PrintMe(string message) //{ // Console.WriteLine("I am a string " + message.GetType().FullName); //} void PrintMe(IFormattable message) { Console.WriteLine("I am a " + message.GetType().FullName); }

Insinuación:

Soy una System.String
Soy un System.Runtime.CompilerServices.FormattableStringFactory + ConcreteFormattableString

Si eliminas comentarios del segundo método obtendrás:

Soy una cadena System.String
Soy una cadena System.String

De acuerdo
Puede que no entienda bien la resolución de sobrecarga, pero 14.4.2 de la especificación de C # implica que el tipo del parámetro pasado se define primero, pero nuevamente, ¿cómo funcionan las lambdas?

void Main() { PrintMe(() => {}); PrintMe(() => {}); } void PrintMe(object doIt) { Console.WriteLine("I am an object"); } //void PrintMe(Expression<Action> doIt) //{ // Console.WriteLine("I am an Expression"); //} void PrintMe(Action doIt) { Console.WriteLine("I am a Delegate"); }

Eliminar comentarios y ...

CS0121 La llamada es ambigua entre los siguientes métodos o propiedades: ''UserQuery.PrintMe (Expresión)'' y ''UserQuery.PrintMe (Acción)''

Así que no entiendo el comportamiento del compilador aquí.

Actualizar:

Para empeorar las cosas, he comprobado este comportamiento para los métodos de extensión:

void Main() { PrintMe("Hello World"); PrintMe($"{"Hello World"}"); "Hello World".PrintMe(); $"{"Hello World"}".PrintMe(); } void PrintMe(object message) { Console.WriteLine("I am a " + message.GetType().FullName); } void PrintMe(IFormattable message) { Console.WriteLine("I am a " + message.GetType().FullName); } public static class Extensions { public static void PrintMe(this object message) { Console.WriteLine("I am a " + message.GetType().FullName); } public static void PrintMe(this IFormattable message) { Console.WriteLine("I am a " + message.GetType().FullName); } }

Ahora lo tengo así:

Soy una System.String
Soy un System.Runtime.CompilerServices.FormattableStringFactory + ConcreteFormattableString
Soy una System.String
Soy una System.String


La nueva sintaxis de cadena interpolada es magia de compilador de parte y clases de tiempo de ejecución de parte.

Revisemos todos los escenarios y veamos lo que realmente está sucediendo.

  1. var s = $"{DateTime.Now}";

    Esto se compila así:

    string s = string.Format("{0}", DateTime.Now);

    Ver Probar Roslyn para más detalles.

  2. string s = $"{DateTime.Now}";

    Esto se compila así:

    string s = string.Format("{0}", DateTime.Now);

    Ver Probar Roslyn para más detalles.

  3. object s = $"{DateTime.Now}";

    Esto se compila así:

    object s = string.Format("{0}", DateTime.Now);

    Ver Probar Roslyn para más detalles.

  4. IFormattable s = $"{DateTime.Now}";

    Esto se compila así:

    IFormattable s = FormattableStringFactory.Create("{0}", new object[] { DateTime.Now });

    Ver Probar Roslyn para más detalles.

  5. FormattableString s = $"{DateTime.Now}";

    Esto se compila así:

    FormattableString s = FormattableStringFactory.Create("{0}", new object[] { DateTime.Now });

    Ver Probar Roslyn para más detalles.

Entonces podemos resumir la magia del compilador de la siguiente manera:

  1. Si podemos sobrevivir con solo usar una string , creada con una llamada a String.Format , entonces haga eso
  2. Si no, use FormattableString y cree uno a través de FormattableStringFactory.Create

Ya que aún no tenemos un documento oficial de normas C # 6, aparte de leer detenidamente los repositorios, temas y discusiones de github, no se conocen las reglas exactas para esto (al menos no para mí, ¡demuéstreme que estoy equivocado!).

Entonces, los ejemplos anteriores muestran qué sucede si el compilador conoce el tipo de destino, en este caso a través del tipo de variable. Si llamamos a un solo método, sin sobrecargas, que tenga uno de esos tipos, sucederá exactamente la misma "magia".

¿Pero qué pasa si tenemos sobrecargas?

Considera este ejemplo:

using System; public class Program { public static void Main() { Test($"{DateTime.Now}"); } public static void Test(object o) { Console.WriteLine("object"); } public static void Test(string o) { Console.WriteLine("string"); } public static void Test(IFormattable o) { Console.WriteLine("IFormattable"); } // public static void Test(FormattableString o) { Console.WriteLine("FormattableString"); } }

Al ejecutar este ejemplo obtenemos esta salida:

string

Claramente, la string sigue siendo la preferida, incluso cuando hay múltiples opciones disponibles.

Vea este violín de .NET para más detalles.

Tenga en cuenta que .NET Fiddle, por alguna razón, no me permite usar FormattableString directamente, pero si ejecuto el mismo código, con esa sobrecarga presente, en LINQPad , todavía obtengo una string como resultado.

Si luego FormattableString sobrecarga de string , obtengo FormattableString , y luego si IFormattable eso obtengo IFormattable , entonces con sobrecargas puedo observar que las reglas son, y aquí paramos con la primera sobrecarga que tiene:

  1. string
  2. FormattableString
  3. IFormattable
  4. object

Larga historia corta:

Si el compilador encuentra un método PrintMe con un parámetro de string , genera este código:

this.PrintMe("Hello World"); this.PrintMe(string.Format("{0}", "Hello World"));

Si comentas el método PrintMe con un parámetro de string , genera este código:

this.PrintMe("Hello World"); this.PrintMe(FormattableStringFactory.Create("{0}", new object[] {"Hello World"}));

Entonces, la parte de la decisión de sobrecarga del método es bastante fácil, supongo.

this.PrintMe("Hello World"); elija el método de parámetro de object , ya que "Hello World" no se puede convertir implícitamente a IFormattable .

Entonces, ¿cuál es el tipo original de cadena interpolada?

Esto se basa en la decisión del compilador:

var s1 = $"{ "Hello World"}";

Genera (como mejor opción):

string s1 = string.Format("{0}", "Hello World");

Y:

void PrintMe(IFormattable message) { Console.WriteLine("I am a " + message.GetType().FullName); } PrintMe($"{ "Hello World"}");

Genera (para que coincida con la firma del método):

this.PrintMe(FormattableStringFactory.Create("{0}", new object[] {"Hello World"}));

Para métodos de extensión:

$"{"Hello World"}".PrintMe(); public static class Extensions { public static void PrintMe(this object message) { Console.WriteLine("I am a " + message.GetType().FullName); } public static void PrintMe(this IFormattable message) { Console.WriteLine("I am a " + message.GetType().FullName); } }

El compilador resuelve $"{"Hello World"}" primero, lo que lleva a una string como una mejor decisión y luego verifica si se ha encontrado un método PrintMe() (se encuentra porque la cadena es un object ). Entonces el código generado es:

string.Format("{0}", "Hello World").PrintMe();

Tenga en cuenta que si elimina el método de extensión para el object , obtendrá un error en tiempo de compilación.


No hagamos las cosas demasiado complicadas.

El tipo de expresión de interpolación de cadena $"..." es string y existe una conversión implícita de una expresión de interpolación de cadena $"..." al tipo System.FormattableString .

El resto es una resolución de sobrecarga de C # ordinaria.

Si se elige una sobrecarga que no necesita la conversión implícita a System.FormattableString , se System.FormattableString una cadena simple (en la práctica, esto se implementa con el método string.Format ). Si se necesita la conversión implícita, se crea una instancia concreta de la clase abstracta System.FormattableString (en la práctica con un método FormattableStringFactory.Create , aunque eso es un detalle de implementación).

No necesita sobrecargas de métodos para ver estos dos casos básicos. Solo haz:

var a = $"..."; // string FormattableString b = $"..."; // the implicit conversion

La diferencia con una expresión lambda como () => { } es que la expresión lambda no tiene un tipo en sí mismo, solo tiene conversiones implícitas. Hay una conversión implícita de la expresión lambda () => { } a cualquier tipo de delegado D que tenga la firma y el tipo de retorno correctos, más una conversión implícita al tipo System.Linq.Expressions.Expression<D> donde D es ese tipo de delegado

var p = () => {}; // BAD, compile-time error Action q = () => {}; // OK, one implicit conversion SomeAppropriateDelType r = () => {}; // OK, another implicit conversion Expression<Action> s = () => {}; // OK, another implicit conversion Expression<SomeAppropriateDelType> t = () => {}; // OK, another implicit conversion

Para completar, aquí está la redacción de la probable especificación de lenguaje C # 6.0 , §7.6.2 (autorizada):

Una expresión de cadena interpolada se clasifica como un valor. Si se convierte inmediatamente a System.IFormattable o System.FormattableString con una conversión de cadena interpolada implícita (§6.1.4), la expresión de cadena interpolada tiene ese tipo. De lo contrario, tiene el tipo de string .

Así que la conversión de cadena interpolada implícita es la designación oficial de la conversión implícita de la que estoy hablando.

La subsección que mencionan §6.1.4 es parte de §6.1 Conversiones implícitas , y lee:

Una conversión de cadena interpolada implícita permite que una expresión de cadena interpolada (§7.6.2) se convierta en System.IFormattable o System.FormattableString (que implementa System.IFormattable ).