Чи слід використовувати .ToString () при об'єднанні змінних рядків і цілих чисел у C #?


19
int a = 1;
int b = 2;
int sum = a + b;
string expression = "Expression: " + a + " + " + b + " = " + sum;
Console.WriteLine(expression); //displays Expression 1 + 2 = 3

Чи варто використовувати:

string expression = "Expression: " + a + " + " + b + " = " + sum;

або

string expression = "Expression: " + a.ToString() + " + " + b.ToString() + " = " + result.ToString();

Чи рекомендується використовувати ToString()при об'єднанні stringі int?


6
У a + "" + b + ""або "" + a + b + "", не має значення: це все об'єднання рядків. У a + b + ""цьому має значення: aі bдодаються першими.
Тім С.

4
Побічна примітка: У VB.NET цю двозначність уникнути, якщо явний оператор конкатенації рядків : "Expression: " + aвикине помилку часу компіляції (з Option Strict On), "Expression: " & aвиконає конкатенацію рядків.
Хайнзі

Перший код призведе до боксу (int до Int32), другий - ні. Другий явно швидше.
Випадкові алфавіти

Відповіді:


33

ToString використання

Ні, ви не повинні використовувати ToStringтут.

З'єднання рядків автоматично перетворює не рядки в рядки, це означає, що два ваші варіанти майже однакові:

Коли один або обидва операнди мають рядковий тип, попередньо визначені оператори додавання об'єднують рядкове представлення операндів.

Джерело: Специфікація мови C #: Оператор додавання, MSDN .

З іншого боку, перший (без ToString):

  • Коротше писати,
  • Коротше читати,
  • Простіше в обслуговуванні та:
  • показує саме намір автора: об'єднати рядки.

Тому віддайте перевагу першому.

Під капотом

Що також цікаво - подивитися, що відбувається під капотом. Один із способів бачити це - дивитися IL-код в межах LINQPad. Ця програма:

void Main()
{
    var a = 3;
    var b = " Hello";
    var c = a + b;
    Console.WriteLine(c);
}

перекладається на наступну ІЛ:

IL_0001:  ldc.i4.3    
IL_0002:  stloc.0     // a
IL_0003:  ldstr       " Hello"
IL_0008:  stloc.1     // b
IL_0009:  ldloc.0     // a
IL_000A:  box         System.Int32
IL_000F:  ldloc.1     // b
IL_0010:  call        System.String.Concat
IL_0015:  stloc.2     // c
IL_0016:  ldloc.2     // c
IL_0017:  call        System.Console.WriteLine

Бачите це System.String.Concat? Це означає, що оригінальний код також може бути записаний так, що перекладається на точно такий же IL:

void Main()
{
    var a = 3;
    var b = " Hello";
    var c = string.Concat(a, b); // This is the line which was changed.
    Console.WriteLine(c);
}

Читаючи документаціюstring.Concat(object[]) , ви можете дізнатися, що:

Метод з'єднує кожен об'єкт у аргументах , викликаючи ToStringметод без параметрів цього об'єкта; він не додає розмежувачів.

Це означає, що ToStringце зайве. Також:

String.Empty використовується замість будь-якого нульового об’єкта в масиві.

Що добре поводиться з випадком, коли деякі операнди є нульовими (див. Виноску 1).

Хоча в останньому прикладі конкатенацію перекладали на string.Concat, слід також виділити оптимізацію компілятора:

var a = "Hello " + "World";

перекладається на:

ldstr       "Hello World"
stloc.0

З іншої сторони:

var a = string.Concat("Hello ", "World");

перекладається на:

ldstr       "Hello "
ldstr       "World"
call        System.String.Concat
stloc.0

Інші альтернативи

Звичайно, є інші способи об'єднання рядкових представлень об'єктів у C #.

  1. StringBuilderвикористовується, коли потрібно виконати багато операцій з об'єднання і сприяє зменшенню кількості створених посередницьких рядків. Вирішити, чи слід використовувати StringBuilderзвичайну конкатенацію, може бути непростим. Скористайтеся профілером або знайдіть відповідні відповіді на переповнення стека.

    Використання StringBuilderмає головний недолік: робить код важким для читання та обслуговування. Для простих випадків, зазначених у вашому питанні, StringBuilderце не тільки шкодить читабельності коду, але й марно з точки зору продуктивності.

  2. string.Join слід використовувати, коли потрібно додати роздільники.

    Очевидно, що ніколи не використовуйте string.Joinпорожній роздільник для об'єднання рядків.

  3. string.Formatможе використовуватися, коли шаблони рядків віддають перевагу конкатенації рядків. Один із випадків, коли ви можете віддати перевагу, це коли локальне повідомлення може бути локалізовано, як це запропоновано у відповіді Кунтетом.

    Використання string.Formatмає кілька недоліків, що робить його непридатним для таких простих випадків:

    • З простими заповнювачами "{0}" часто незрозуміло, який параметр куди йде. Часто помилково перевертати параметри або забути їх. На щастя, C # 6 нарешті вводить рядкову інтерполяцію, яка вирішує цю проблему.

    • Продуктивність роботи може погіршити. Звичайно, не вважайте, що string.Formatце завжди повільніше. Якщо результативність має значення, виміряйте два підходи та визначте, який з них швидший, виходячи з ваших фактичних результатів замість припущень.

    • Код трохи довший для запису, довший для читання і складніше в обслуговуванні, хоча це надзвичайно незначно і не повинно турбувати вас занадто.


¹ Різниця з’являється, коли є один із об’єктів null. Без цього ToStringa nullзамінюється порожнім рядком. З ToString, NullReferenceExceptionкидається.


1
Саме з цієї причини (непередбачувана поведінка) поєднання рядків із символом +вважається поганою практикою у більшості мов. У цьому випадку я б використовував string.Concat, у багатьох випадках string.Formatкраще. Це, звичайно, не є Pythonic для використання +, і оскільки PEP 3101 також %не перешкоджає цьому str.format.
Арда Сі

2
Чи заміщення порожнього рядка nullсправді "красивим поводженням з ним"? Ну, це не так вже й погано, як "на помилку відновити наступну" ...
Deduplicator

Під кришкою, якщо ви об'єднаєтесь без виклику .ToString (), бокс відбудеться ..., і Concat(object[])буде використовуватися замість Concat(string[]). Тож "string " + iнасправді не тотожне"string " + i.ToString()
Випадкові алфавіти

@RandomAlphabets: дійсна точка. Однак різниця полягає просто в розташуванні ToString(): код ОП в одному випадку, System.String.Concatреалізація в іншому випадку. Отже, відповідь залишається вірною: не пишіть код, який не має ніякої користі.
Арсеній Муренко

15

Натомість слід скористатися форматним рядком. Простіше відформатувати свій номер у рядковому поданні чи локалізації. наприклад:

string expression = string.Format("Expression: {0} + {1} = {2}", a, b, sum);

Більше інформації про MSDN .

Однак форматор рядків менш читабельний (а може бути і продуктивність), ніж конкатенація рядків.


6
Потрібно пояснити, чому такий варіант краще?
Світовий інженер

@WorldEngineer: я оновив пояснення.
кунцет

12
Я не знаю про всіх інших, але насправді я вважаю це більш читабельним. Можливо тому, що я виріс із C, де s [n] printf - єдиний реалістичний варіант для такого роду коду.
Жуль

2
Але цей спосіб дає три джерела помилок замість одного. Коли ви щось додаєте в рядок, вам потрібно 1) змінити рядок формату, 2) не забути додати новий параметр у список, 3) спробувати вибрати правильне місце у списку, де слід розмістити новий параметр.
Руслан

2
Просто хочете звучати і повідомляти про майбутню функцію інтерполяції рядків у C # 6. Ви напишете "Вираз: \ {a} + \ {b} = \ {sum}", щоб отримати той самий результат. codeproject.com/Articles/846566/…
cwap

-2

Якщо ви використовуєте +конкатенацію, вона сама використовує String.Concatметод. Сам рядок не виставляє + оператора.

наприклад:

int i = 10;
string str = "hello" + i;

складається в:

int i = 10;
object o1 = "hello";
object o2 = i; // Note boxing
string str = string.Concat(o1, o2);

Якщо ви безпосередньо зателефонуєте ToString, це дозволить уникнути боксу та викликати Concat(string, string)перевантаження. Отже, ToStringвиклик буде дещо ефективнішим, хоча і недостатньо значним. Ви краще підете з прикладом, який, на вашу думку, читабельніше.


2
це , здається, тільки повторні пункти , зроблених і пояснено в попередньому рівні відповіді : «Об'єднання рядків автоматично перетворити не-рядок в рядки ...»
комар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.