Бокс и Unboxing в String.Format(...)... следующие рационализированы?
Я читал о боксе/распаковке, и получается, что если вы делаете обычный String.Format()
, где у вас есть тип значения в вашем списке аргументов object[]
, это вызовет операцию бокса. Например, если вы пытаетесь распечатать значение целого числа и сделать string.Format("My value is {0}",myVal)
, он вставит ваш myVal
int
в поле и запустит на нем функцию ToString
.
Просмотр, Я нашел эту статью.
Похоже, вы можете избежать штрафа за бокс, просто выполнив .ToString
по типу значения, прежде чем передавать его в строку. Функция Format: string.Format("My value is {0}",myVal.ToString())
- Это правда? Я склонен полагать, что автор
доказательства.
- Если это так, почему компилятор просто не делает этого
для тебя? Может быть, это изменилось с 2006 года? Кто-нибудь знает? (У меня нет времени/опыта, чтобы выполнить весь анализ IL).
Ответы
Ответ 1
Компилятор не делает этого для вас, потому что string.Format
принимает params Object[]
. Бокс происходит из-за перехода на Object
.
Я не думаю, что компилятор имеет тенденцию использовать специальные методы case, поэтому он не будет удалять бокс в таких случаях.
Да, во многих случаях верно, что компилятор не будет делать бокс, если сначала вызовете ToString()
. Если он использует реализацию из Object
, я думаю, что все равно придется вставлять.
В конечном счете синтаксический разбор string.Format
самой строки формата будет намного медленнее, чем любая операция по боксу, поэтому накладные расходы незначительны.
Ответ 2
1: да, до тех пор, пока переопределяет тип значения ToString()
, который делает все встроенные типы.
2: потому что в спецификации нет такого поведения, и правильная обработка params object[]
(типов значений по-адресу): бокс
string.Format - это как любой другой непрозрачный метод; тот факт, что он собирается сделать это, непрозрачен для компилятора. Это также было бы функционально некорректно, если шаблон включал формат, например {0:n2}
(для которого требуется конкретное преобразование, а не только ToString()
). Попытка понять шаблон нежелательна и ненадежна, поскольку шаблон не может быть известен до времени выполнения.
Ответ 3
Лучше избегать бокса, построив строку с помощью StringBuilder или StringWriter и используя типизированные перегрузки.
В большинстве случаев бокс должен быть немного обеспокоен и не стоит того, чтобы вы даже об этом знали.
Ответ 4
Легкий первый. Причина, по которой компилятор не превращает string.Format("{0}", myVal)
в string.Format{"{0}", myVal.ToString())
, заключается в том, что нет причин, почему это необходимо. Должно ли оно превратить BlahFooBlahBlah(myVal)
в BlahFooBlahBlah(myVal.ToString())
? Возможно, это будет иметь тот же эффект, но для лучшей производительности, но, скорее всего, это приведет к ошибке. Плохой компилятор! Нет печенья!
Если что-то не может быть обосновано из общих принципов, компилятор должен оставить в покое.
Теперь для интересного бит ИМО: Почему первые бокс и последний не.
Для первого, поскольку единственная сигнатура соответствия string.Format(string, object)
, целое число должно быть обращено в объект (в штучной упаковке), который должен быть передан методу, который ожидает получить строку и объект.
Другая половина этого, хотя, почему поле myVal.ToString()
тоже?
Когда компилятор приходит к этому фрагменту кода, он имеет следующие знания:
- myVal - это Int32.
- ToString() определяется Int32
- Int32 - это тип значения, и поэтому:
- myVal не может быть нулевой ссылкой * и:
- Не может быть более производного переопределения - Int32.ToString() эффективно закрывается.
Теперь, как правило, компилятор С# использует callvirt
для всех вызовов методов по двум причинам. Во-первых, вы иногда хотите, чтобы это был виртуальный вызов. Во-вторых, (более спорно), они решили запретить любой вызов метода на нулевой ссылки и <Т26 > имеет встроенный тест для этого.
В этом случае, однако, ни одно из них не применяется. Не может быть более производного класса, который переопределяет Int32.ToString(), а myVal не может быть нулевым. Поэтому он может ввести метод call
в метод ToString()
, который пропускает Int32
без бокса.
Эта комбинация (значение не может быть нулевым, метод не может быть переопределен в другом месте) только решает типы ссылок гораздо реже, поэтому компилятор не может воспользоваться таким преимуществом (тогда он также не будет так как они не должны быть в коробке).
Это не тот случай, если Int32
наследует метод реализации. Например, myVal.GetType()
будет вставлять myVal
, поскольку переопределение Int32
не может быть, оно не может быть виртуальным, поэтому доступ к нему можно получить, обработав myVal
как объект, поместив его в бокс.
Тот факт, что это означает, что компилятор С# будет использовать callvirt
для не виртуальных методов, а иногда call
для виртуальных методов, не без степени иронии.
* Обратите внимание, что даже нулевое целое число, установленное в null, не совпадает с нулевой ссылкой в этом отношении.
Ответ 5
Почему бы не попробовать каждый подход сто миллионов раз или около того и посмотреть, сколько времени потребуется:
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
int myVal = 6;
sw.Start();
for (int i = 0; i < 100000000; i++)
{
string string1 = string.Format("My value is {0}", myVal);
}
sw.Stop();
Console.WriteLine("Original method - {0} milliseconds", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
string string2 = string.Format("My value is {0}", myVal.ToString());
}
sw.Stop();
Console.WriteLine("ToStringed method - {0} milliseconds", sw.ElapsedMilliseconds);
Console.ReadLine();
}
На моей машине я нахожу, что версия .ToStringed работает примерно в 95% случаев, когда исходная версия берет, поэтому некоторые эмпирические данные для небольшой производительности приносят пользу.
Ответ 6
string.Format("My value is {0}", myVal)<br>
myVal is an object<br><br>
string.Format("My value is {0}",myVal.ToString())<br>
myVal.ToString() is a string<br><br>
ToString перегружен, и поэтому компилятор не может решить для вас.
Ответ 7
Я нашел проект StringFormatter на GitHub. Описание звучит очень многообещающе.
Встроенные средства форматирования строк в .NET надежны и удобны в использовании. К сожалению, они также выполняют смешное количество распределений GC. В основном они недолговечны, а на настольном ГХ они вообще не заметны. Однако в более ограниченных системах они могут быть болезненными. Кроме того, если вы пытаетесь отслеживать использование GC с помощью оперативных отчетов в своей программе, вы можете быстро заметить, что попытки распечатать текущее состояние GC приводят к дополнительным выделениям, что сводит на нет всю попытку инструментария.
Таким образом, существует эта библиотека. Это не полностью выделение бесплатно; Есть несколько разовых затрат на установку. Устойчивое состояние, однако, полностью без распределения. Вы можете свободно использовать утилиты форматирования строк в основном цикле игры, не вызывая при этом постоянного мусора.
Я быстро проверил интерфейс библиотеки. Вместо пакета параметров автор использует функции с определенными вручную generic
аргументами. Что совершенно логично для меня, если вы заботитесь о мусоре.