Si tengo una cadena de formato que utiliza el mismo marcador de posición varias veces, como:
emailBody = $"Good morning {person.GetFullName()}, blah blah blah, {person.GetFullName()} would you like to play a game?"; ¿ person.GetFullName() se evalúa dos veces, o el compilador es lo suficientemente inteligente como para saber que estos son el mismo valor y deben evaluarse una vez?
Sí, se evaluará dos veces. No puede saber que es el mismo valor. Por ejemplo:
Random rng = new Random(); Console.WriteLine($"{rng.Next(5)}, {rng.Next(5)}, {rng.Next(5)}");Es exactamente lo mismo que si solo usara la expresión como argumento del método, si llamara:
Console.WriteLine("{0} {1}", person.GetFullName(), person.GetFullName()); ... esperaría que GetFullName se llamara dos veces entonces, ¿no es así?
Si solo quieres evaluarlo una vez, hazlo antes:
var name = person.GetFullName(); emailBody = $"Good morning {name}, blah blah blah, {name} would you like to play a game?"; O simplemente use una string.Format normal. Llamada de formato:
emailBody = string.Format( "Good morning {0}, blah blah blah, {0} would you like to play a game?", person.GetFullName());¿Es el compilador lo suficientemente inteligente como para saber que tienen el mismo valor [...]?
Pero no será necesariamente el mismo valor. El método podría muy bien devolver resultados diferentes en invocaciones posteriores, por lo que no puede almacenar en caché el resultado.
La mayoría de las veces, el compilador podrá optimizar esto. Desafortunadamente, los momentos en que no puede son también los momentos en los que más le importaría.
La optimización en este contexto no se basa en llamar a la misma función dos veces, sino en la inserción. Los accesores suelen ser triviales (buscar un campo o, como máximo, realizar una conversión entre tipos numéricos o formatear/analizar entre numérico y tipo de cadena) y, como tales, cumplen fácilmente los criterios para la inserción. Además, el JIT evita problemas con la incorporación de módulos cruzados. Por otro lado, las interfaces, los delegados y las llamadas virtuales pueden evitar la incorporación.
Una vez que el compilador ha reemplazado ambas llamadas por el cuerpo de la función, ve dos cargas del mismo campo y probablemente puede usar la optimización de subexpresiones comunes para recuperar el campo solo una vez (siempre que pueda probar que no hay una escritura intermedia desde el mismo campo). subproceso... en particular, no tiene que preocuparse por las escrituras de otros subprocesos a menos que haya un bloqueo intermedio).
Pero esto solo ahorra una búsqueda de campo adicional. En los casos en los que la llamada de acceso es costosa (llamada a procedimiento remoto o búsqueda en la base de datos), no será posible realizar la inserción y tendrá que pagar por dos evaluaciones. En tal caso, vale la pena almacenar en caché el resultado usted mismo.