Избыточный Object.ToString

Я просматриваю следующий код.

private string MakeKey(int SnapshotID, string UserName, string BrandName)
{
    return string.Format("{0}:{1}:{2}", SnapshotID.ToString(), UserName, BrandName);
}

Метод SnapshotID.ToString() повторно вызывает метод ToString().
Какие потенциальные проблемы могут возникнуть? например скорость.

EDIT
Я добавил тест, если кто-то хочет уточнить: http://pastebin.com/gynNjwT1

With ToString:      0.010884
Without ToString:   0.001446

With ToString:      0.005506
Without ToString:   0.002852

With ToString:      0.001155
Without ToString:   0.009117

With ToString:      0.003210
Without ToString:   0.001546

person Ashley Medway    schedule 12.12.2013    source источник
comment
Но SnapshotID это int. Как SnapshotID может быть null?   -  person Soner Gönül    schedule 12.12.2013
comment
int не может быть null. Format все равно будет просто вызывать ToString на входах (если не нуль), насколько я помню.   -  person Grant Thomas    schedule 12.12.2013
comment
В этом коде... нет. В случае, если Format() используется с IFormatProvider, явный ToString() переопределит его.   -  person Adriano Repetti    schedule 12.12.2013
comment
@SonerGönül Да, я только что заметил, что это будет обновляться   -  person Ashley Medway    schedule 12.12.2013
comment
Этот вопрос не очень ясен... о какой версии .NET идет речь? И почему скорость важна в этой сфере? Для меня это похоже на дополнительную оптимизацию.   -  person Jocke    schedule 12.12.2013
comment
@Jocke .NET 4.5, я не говорю, что доли миллисекунды будут иметь значение для моего приложения, это скорее действительно это имеет значение, а не то, насколько   -  person Ashley Medway    schedule 12.12.2013


Ответы (3)


В этом коде... нет, он просто избыточен и даже немного медленнее, потому что String.Format() снова вызовет ToString() на самом string.

В случае, если String.Format() используется с IFormatProvider, явное ToString() переопределит его.

Правки (из комментариев): примечание о производительности. Это может быть или не быть медленнее (бесполезный вызов ToString()) по сравнению с боксом примитивного типа (int). Мы должны измерить это, но это довольно сложно сказать. Что медленнее? Вызов виртуальной функции или бокс? Имеет ли это значение по сравнению с String.Format() общим временем? На это уже отвечено здесь, на SO . IMO, если это было сделано как умная оптимизация, это довольно бесполезно.

Представление

OP сделал для этого небольшой тест (см. комментарии), просто для развлечения, результаты на моей тестовой машине:

Test               Time [ms]
With ToString      0.002929
Without ToString:  0.003414

Я переписал тест, чтобы не использовать потоки (потому что он добавит больше переменных, ядра могут отличаться, и ОС будет загружать их динамически). С этим (более простым!) Тестовым кодом (полученным из теста OP):

Stopwatch sw = new Stopwatch();
sw.Start();
for (int x = 0; x < Count; x++)
{
    _temp = MakeKeyToString(1, "Ashley", "MyBrand");
}
sw.Stop();
TimeSpan test = TimeSpan.FromMilliseconds((double)sw.ElapsedMilliseconds);

Я понимаю, что версия ToString() быстрее (для Count = 1000000) в среднем около 10/20 наносекунд. IMO, чтобы измерить что-то настолько маленькое, нам нужна гораздо лучшая тестовая среда и более профессиональный подход. Изменение его кода для String.Format() для использования IFormatProvider:

string MakeKeyToString(int SnapshotID, string UserName, string BrandName)
{
    return string.Format(CultureInfo.InvariantCulture,
        "{0}:{1}:{2}", SnapshotID.ToString(), UserName, BrandName);
}

Снова все меняем: без ToString() быстрее на 200 наносекунд. Опять же слишком мало, чтобы измерить таким образом.

Выводы

Вы даже не можете начать рассматривать это как оптимизацию, слишком много факторов будет влиять на это, и это настолько мало, что останется незамеченным по сравнению с общим String.Format() временем. Что еще хуже, это может привести к тонким ошибкам, если вы измените String.Format() на использование IFormatProvider, потому что это заставит вас остановиться и подумать: "Почему это? Должна быть причина, связанная с культурой. Может быть...", когда на самом деле...( наверное) нет.

person Adriano Repetti    schedule 12.12.2013
comment
Будет ли это медленнее, чем накладные расходы на упаковку int? - person Lukazoid; 12.12.2013
comment
@Lukazoid, чтобы быть уверенным, что мы должны его измерить, но бокс должен быть довольно быстрым (даже если второй вызов ToString() может быть оптимизирован из-за дрожания). Кстати, они оба (бокс и двойной ToString()) настолько малы (по сравнению с самим Format(), что здесь трудно говорить о производительности). Если это было сделано как умная оптимизация, то IMO они потерпели неудачу. - person Adriano Repetti; 12.12.2013
comment
Я полностью согласен, просто пытаюсь представить это как возможное оправдание. - person Lukazoid; 12.12.2013
comment
возможно, вам будет интересно увидеть, что pastebin уже готов и может быть улучшен pastebin.com/gynNjwT1 - person Ashley Medway; 12.12.2013
comment
stackoverflow.com/questions/8477322/ - person Jocke; 12.12.2013
comment
@Adriano Посмотрите мои результаты в Вопросе, это немного странно? - person Ashley Medway; 12.12.2013
comment
@AshleyMedway Не совсем, в среднем без ToString кажется немного быстрее, но (если каждый кортеж представляет собой отдельный запуск) результаты довольно нестабильны (как мы можем ожидать от теста, в котором измеренный код такой маленький и быстрый) . Слишком много факторов будет играть с этим, ИМО, чтобы сделать надежный тест довольно сложно. - person Adriano Repetti; 12.12.2013
comment
@AshleyMedway переписывает тест, чтобы он был последовательным (без участия потоков, потому что ядра ЦП могут быть разными, и ОС может использовать их для разных задач). Я получаю, что ToString() работает быстрее - в среднем для 1000000 испытаний - 10/20 наносекунд. Я думаю, что это почти предел того, что мы можем измерить без профессионального подхода. - person Adriano Repetti; 12.12.2013
comment
нужно помнить, что первый вызов всегда будет самым медленным, так как он должен выполнять JIT и т. д., но последующие вызовы будут нормальными. - person Ahmed ilyas; 12.12.2013
comment
@Ahmedilyas да, поэтому я использовал 1000000 повторений. - person Adriano Repetti; 12.12.2013
comment
Эти тесты производительности не учитывают GC; накладные расходы на упаковку приведут к гораздо большему количеству выделений кучи, которые необходимо будет собрать GC. Однако я думаю, что мы все видим, что это ненужная микрооптимизация. - person Lukazoid; 13.12.2013
comment
@Lukazoid абсолютно прав, я не думал о GC, но для реального применения он сыграет свою роль! - person Adriano Repetti; 13.12.2013

В этом примере, вызывая Int32.ToString(), вы избегаете накладных расходов на упаковку вашего int в Object.

Однако накладные расходы на вызов .ToString(), а затем вызов String.Format на .ToString() могут перевешивать накладные расходы на упаковку. И все это ничтожно по отношению к String.Format.

person Lukazoid    schedule 12.12.2013
comment
И разве строка не упакована в объект? - person Tobia Zambon; 12.12.2013

Нет, код избыточен и стоп. Также SnapshotID не может быть нулевым, поскольку это целое число, а не целое число, допускающее значение NULL (int?). В этом случае метод ToString будет вызываться дважды, я думаю (один явно и один в результате первого вызова), поэтому, возможно, могут возникнуть некоторые различия в производительности.

person Tobia Zambon    schedule 12.12.2013