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

Бокс и 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).
4b9b3361

Ответ 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 аргументами. Что совершенно логично для меня, если вы заботитесь о мусоре.