Подтвердить что ты не робот

Использует ли интерполяция строк дублируемое использование?

Если у меня есть строка формата, которая использует один и тот же держатель места несколько раз, например:

emailBody = $"Good morning {person.GetFullName()}, blah blah blah, {person.GetFullName()} would you like to play a game?";

делает person.GetFullName() получить оценку дважды, или компилятор достаточно умный, чтобы знать, что это одно и то же значение, и должно оцениваться один раз?

4b9b3361

Ответ 1

Да, он будет оцениваться дважды. Он не может знать, что это одно и то же значение. Например:

Random rng = new Random();
Console.WriteLine($"{rng.Next(5)}, {rng.Next(5)}, {rng.Next(5)}");

Это точно так же, как если бы вы использовали выражение как аргумент метода - если вы вызывали:

Console.WriteLine("{0} {1}", person.GetFullName(), person.GetFullName());

... вы ожидали бы, что GetFullName будет вызываться дважды, не так ли?

Если вы хотите только один раз оценить его, сделайте это заранее:

var name = person.GetFullName();
emailBody = $"Good morning {name}, blah blah blah, {name} would you like to play a game?";

Или просто используйте обычный вызов string.Format:

emailBody = string.Format(
    "Good morning {0}, blah blah blah, {0} would you like to play a game?",
    person.GetFullName());

Ответ 2

является компилятором достаточно умным, чтобы знать, что это одно и то же значение [...]?

Но это не обязательно будет одно и то же значение. Метод может очень хорошо возвращать разные результаты при последующих вызовах, поэтому он не может кэшировать результат.

Ответ 3

В большинстве случаев компилятор сможет оптимизировать это. К сожалению, времена, когда это не так, - это время, которое вам больше всего понравится.

Оптимизация в этом контексте не основана на вызове одной и той же функции дважды, а на основе inline. Аксессоры обычно тривиальны (поиск поля или, самое большее, преобразование между числовыми типами или формат/разбор между цифровым и строковым типом) и, как таковой, легко соответствуют критериям для inline. Кроме того, JIT предотвращает проблемы с кросс-модульной вставкой. С другой стороны, интерфейсы, делегаты и виртуальные вызовы могут предотвратить inlining.

После того как компилятор заменил оба вызова телом функции, он видит две загрузки из одного и того же поля и, вероятно, может использовать общую оптимизацию подвыражения, чтобы на самом деле извлекать поле только один раз (пока он может доказать, что нет промежуточной записи из того же потока... в частности, он не должен беспокоиться о записи из других потоков, если нет промежуточной блокировки).

Но это только сохраняет дополнительную выборку поля. В случаях, когда вызов доступа дорого (удаленный вызов процедуры или поиск базы данных), вложение будет невозможно, и вы заплатите за две оценки. В таком случае кеширование результата само по себе стоит.

Ответ 4

У компилятора нет способа убедиться, что метод всегда будет возвращать одно и то же значение. Если вам нужна такая оптимизация, у вас есть два варианта:

Используйте переменную:

string fullName = person.GetFullName();
emailBody = $"Good morning {fullName}, blah blah blah, {fullName} would you like to play a game?";

Использование String.Format:

emailBody = String.Format("Good morning {0}, blah blah blah, " +
            "{0} would you like to play a game?", person.GetFullName();