Чому / коли було б доречно перекрити ToString?


103

Я вивчаю C # і мені цікаво, в чому сенс і користь переосмислення ToString, як показано в прикладі нижче.

Чи можна це зробити більш простим способом, використовуючи загальний метод без перебору?

public string GetToStringItemsHeadings
{
    get { return string.Format("{0,-20} {1, -20}", "Office Email", "Private Email"); }
}


public override string ToString()
{
    string strOut = string.Format("{0,-20} {1, -20}", m_work, m_personal);
    return strOut;
}

4
Залежить, чи дійсно ви хочете використовувати його для відображення об'єкта в інтерфейсі де завгодно, інакше це звичайно лише для налагодження - ви побачите вихід ToString у вікні перегляду VS. (Але ви також можете досягти цього за допомогою атрибутів класу.) Враховуючи, що у вас є заголовки та вихід, схоже, що ця програма використовує його для скидання об'єкта за допомогою Console.WriteLine(obj.GetToStringItemsHeadings); Console.WriteLine(obj);або подібного.
Rup

2
Я не дуже розумію питання. Чи можна це зробити інакше? Так. Але чому ти хочеш це робити інакше.
Конрад Рудольф

5
Я б на самому справі пропустити GetToStringім'я властивості ... Тоді ви отримаєте, наприкладConsole.WriteLine(obj.ItemHeadings)
Svish

і змінити властивість ItemHeadings на статичну, оскільки воно не потребує жодного "цього".
eFloh

Ви можете перевірити це питання: stackoverflow.com/questions/7906828/oo-design-advice-tostring
Юрій Ghensev

Відповіді:


127
  • Вам потрібно переосмислити ToString? Немає.

  • Чи можете ви отримати рядкове представлення вашого об'єкта іншим способом? Так.

Але при використанні ToStringви використовуєте метод, загальний для всіх об'єктів, і тому інші класи знають про цей метод. Наприклад, щоразу, коли .NET Framework хоче перетворити об'єкт у рядкове представлення, ToStringвін є головним кандидатом (є й інші, якщо ви хочете надати більш детальні варіанти форматування).

Конкретно,

Console.WriteLine(yourObject);

закликав би yourObject.ToString().


13
Так. Також це дуже корисно в розробці MVC або ASP.Net, де @yourObjectі <%=yourObject%>збираються "ToString-ed".
Бен Леш

4
Також під час заповнення
комбінованої скриньки

3
Остерігайтеся такої практики. Справжня історія: студент замінив ToString методом, який, крім повернення рядка, змінив дані. Він налагоджував програму, але поки програма стояла на точці розриву, значення продовжувало змінюватися. Мабуть - кожен раз, коли він перевіряв, чи виконується значення ToString - значення змінювалось, навіть якщо програма не працювала.
JNF

3
@JNF Що таке "така практика"? ToStringнасправді не повинно змінювати стан об'єкта. Але це не те, що я виступав.
Конрад Рудольф

8
@JNF Я не приймаю цього. ToStringце означає бути перевизначені. Період. Поставити застереження щодо цього твердження не є результативним. Якщо ви робите це неправильно - власна вина. Насправді, ваш студент порушив пункт 4 в офіційному списку керівних принципів щодо відміни, ToStringякий був розміщений у відповіді Девіда Андерсона.
Конрад Рудольф

129

Я просто дам вам відповідь прямо з Рамкових керівних принципів із серії .NET Development Series.

УНИКНІТЬ викидання виключень ізToString

CONSIDER повертає унікальний рядок, пов’язаний із екземпляром.

ВІДПОВІДЬ, що має вихід, ToStringє дійсним входом для будь-яких методів розбору цього типу.

Упевніться, що ToStringнемає помітних побічних ефектів.

DO звіт безпеки конфіденційної інформації через перевизначення ToStringтільки після того, як вимагає відповідного дозволу. Якщо запит на дозвіл не вдається, поверніть рядок, що виключає інформацію, що стосується безпеки.

Object.ToStringМетод призначений для використання для загального відображення і налагодження. Реалізація за замовчуванням просто надає ім'я типу об'єкта. Реалізація за замовчуванням не дуже корисна, і рекомендується перекрити метод.

НЕ переосмислюйте, ToStringколи можна повернути цікаву людину, читану рядок. Реалізація за замовчуванням не дуже корисна, і спеціальна реалізація майже завжди може забезпечити більше значення.

DO воліє зрозуміле ім'я більш унікальним , але не читаються ID.

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

НЕ намагайтеся, щоб рядок повернувся з ToStringкороткого. Налагоджувач використовує ToStringдля отримання текстового зображення об'єкта, який буде показаний розробнику. Якщо рядок довший, ніж може відображати налагоджувач, перешкоджає налагодження.

DO форматирування рядків на основі поточної культури потоку при поверненні інформації, що залежить від культури.

НЕ забезпечуйте перевантаження ToString(string format)або впроваджуйте IFormattable, якщо повернення рядка ToStringє чутливим до культури або є різні способи форматування рядка. Наприклад, DateTimeпередбачена перевантаження та реалізація IFormattable.

НЕ повертайте порожній рядок або нуль зToString

Я клянусь цими вказівками, і ви повинні. Я не можу сказати вам, як мій код покращився саме завдяки цій одній інструкції ToString. Те ж саме стосується таких речей, як IEquatable(Of T)і IComparable(Of T). Ці речі роблять ваш код дуже функціональним, і ви не пошкодуєте, витративши додатковий час на його реалізацію.

Особисто я ніколи не використовував ToString багато для користувальницьких інтерфейсів, я завжди виявляв властивість чи метод певного роду. Більшу частину часу ви повинні використовувати ToStringдля налагодження та цілей розробника. Використовуйте його для відображення важливої ​​діагностичної інформації.


6
Приємна відповідь, чи це вказівки, на які ви посилаєтеся msdn.microsoft.com/en-us/library/ms229042.aspx ?
Джодрелл


6
Потрібно краще цитування.
Бен Войгт

13
Я не знав, що SO перетворюється на Вікіпедію.
Девід Андерсон

14
Це не так, але коли ви цитуєте вказівки, надання джерела є незаперечним плюсом.
Фаланве

39

Переопределення ToString()дозволяє надати корисне для людини читане рядкове представлення класу.

Це означає, що результати можуть розкрити корисну інформацію про ваш клас. Наприклад, якщо у вас був клас Person, ви можете вибрати ToString()вихідний ідентифікатор особи, їх ім'я, прізвище тощо. Це надзвичайно корисно при налагодженні або вході в систему.

Що стосується вашого прикладу - важко сказати, чи корисний ваш перебір, не знаючи, що це за клас - але сама реалізація нормальна.


13

Це завжди доречно, але уважно розглядайте наміри, що стоять за вашим проявом

Краще питання було б задати:

Чому один би перекрив ToString ()?

ToString () - це вікно у стан об'єкта. Акцент на державі як вимозі. Сильно мови OOP, як Java / C #, зловживають моделлю OOP, інкапсулюючи все в класі. Уявіть, що ви кодуєте мовою, яка не відповідає сильній моделі OOP; Подумайте, чи використовували б ви клас чи функцію. Якщо ви використовуєте його як функцію (тобто дієслова, дії), а внутрішній стан підтримується лише тимчасово між входом / виходом, ToString () не додасть значення.

Як уже згадували інші, важливо врахувати, що ви виводите з ToString (), оскільки це може бути використане налагоджувачем або іншими системами.

Мені подобається уявляти метод ToString як параметр --help об'єкта. Він повинен бути коротким, читабельним, очевидним та легким для відображення. Він повинен показати , що об'єкт є не те , що він робить . Зважаючи на це, давайте розглянемо ...

Використовуйте регістр - розбір пакета TCP:

Не мережеве захоплення на рівні додатків, а щось із більшою кількістю м'яса, як захоплення pcap.

Ви хочете перевантажити ToString () лише для рівня TCP, щоб ви могли надрукувати дані на консоль. Що б воно включало? Ви можете зійти з розуму і проаналізувати всі деталі TCP (тобто TCP є складним) ...

Що включає:

  • Джерело Порт
  • Порт призначення
  • Порядковий номер
  • Номер підтвердження
  • Зсув даних
  • Прапори
  • Зміщення вікон
  • Контрольна сума
  • Терміновий покажчик
  • Варіанти (я навіть не збираюся туди їхати)

Але чи хотіли б ви отримати всі ці сміття, якби дзвонили TCP.ToString () на 100 пакетів? Звичайно, ні, це було б перевантаження інформацією. Легкий і очевидний вибір - це також найрозумніший ...

Розкрийте, що люди очікують побачити:

  • Джерело Порт
  • Порт призначення

Я віддаю перевагу розумному результату, який людині легко розібрати, але YMMV .

TCP:[destination:000, source:000]

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

Але що з усією рештою цієї соковитої інформації, про яку я говорив раніше, хіба це теж не корисно? Я доберусь до цього, але спочатку ...


ToString () - один з найцінніших і недостатньо використовуваних методів усіх часів

З двох причин:

  1. Люди не розуміють, для чого призначений ToString ()
  2. У базовому класі "Object" відсутній інший, не менш важливий, рядковий метод.

Причина 1 - Не зловживайте корисністю ToString ():

Дуже багато людей використовують ToString (), щоб намалювати просте рядкове зображення об’єкта. Посібник C # навіть говорить:

ToString - основний метод форматування в .NET Framework. Він перетворює об'єкт у його рядкове подання, щоб він був придатним для відображення.

Відображення, не подальша обробка. Це не означає, візьміть моє гарне рядкове представлення пакету TCP вище та витягніть вихідний порт за допомогою regex :: cringe ::.

Право спосіб робити речі є, ToString виклик () безпосередньо на властивості SourcePort (який до речі є USHORT так ToString () вже повинні бути доступні).

Якщо вам потрібно щось більш надійне для упаковки стану складного об'єкта для машинного розбору, вам буде краще використовувати структуровану стратегію серіалізації.

На щастя, такі стратегії є дуже поширеними:

  • Імеріалізаційний (C #)
  • Соління (Python)
  • JSON (Javascript або будь-яка мова, яка реалізує його)
  • Мило
  • тощо ...

Примітка: Якщо ви не використовуєте PHP, тому що, herp-derp, є функція для цього: snicker ::

Причина 2 - ToString () недостатньо:

Я ще не бачив мови, яка реалізує це в основі, але я бачив і використовував варіанти цього підходу в дикій природі.

Деякі з яких включають:

  • ToVerboseString ()
  • ToString (багатослівний = вірно)

В основному, цей волохатий безлад стану пакету TCP повинен бути описаний для людської читабельності. Щоб уникнути "побиття мертвого коня", кажучи про TCP, я "вкажу пальцем" на випадок №1, де я думаю, що ToString () і ToVerboseString () недостатньо використовуються ...

Використовуйте корпус - масиви:

Якщо ви в основному використовуєте одну мову, вам, мабуть, комфортно підходити до цієї мови. Для таких людей, як я, які стрибають між різними мовами, кількість різноманітних підходів може дратувати.

Тобто, кількість разів мене це дратувало більше, ніж сума всіх пальців кожного індуїстського бога разом.

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

Мене просять дуже простий підхід:

print(array.ToString());

Виходи: 'масив [x]' або 'масив [x] [y]'

Де x - кількість елементів у першому вимірі, а y - кількість елементів у другому вимірі або якесь значення, яке вказує на те, що 2-й розмір є нерівним (можливо, min / max діапазон?).

І:

print(array.ToVerboseString());

Виводить весь баг в чудовому друку, тому що я ціную гарні речі.

Сподіваюся, це проливає трохи світла на тему, яка мене дратувала вже давно. Принаймні, я набризнув трошки принадою для PHPers, щоб спростувати цю відповідь.


Ви писали: "Я ще не бачив мови, яка реалізує це в основі". Мені здається, що Python досить близько до цього; наприклад, друк масиву дає всі свої дані стисло. Це одне, що мені не вистачає в C # / Java, хоча мені подобається їхня суворість. І коли вам потрібно вийти за межі ToString (), було б непогано мати щось на зразок синтаксису розуміння списку Python; Я думаю, що string.Join () схожий, хоча і менш гнучкий.
Джон Кумбс

1
@JCoombs Я не згоден, розуміння чудові. Переміщення структур даних в Python за допомогою розуміння значно полегшує життя, але такий підхід все ще вимагає глибокого знання внутрішньої структури об'єкта. Що не завжди очевидно в класах, імпортованих із зовнішньої бібліотеки. Автори бібліотеки є хорошою практикою переосмислювати ToString () та надавати корисне для людини читане представлення, але також було б добре мати загальний метод демпінгу, який виводить структуру об'єкта в загальноструктурованому людському читаному форматі (колишній JSON).
Еван Плейс

Відмінний момент. Таким чином, замість того, щоб показувати нічого іншого, ніж в основному типуof, він може автоматично серіалізуватися на щось на зразок JSON. (Поки що я бачив лише об’єкти .NET, серіалізовані в XML, але я новачок у .NET) Але тоді небезпека може бути спокусою розглядати ToString () як машиночитаний? (Напр., Очікуємо, що це зможе деаріалізувати.)
Джон Кумбс,

1
@JCoombs Так, JSON і XML на функціональному рівні служать одній і тій же цілі. Дуже багато людей використовують ToString як машиночитаний вихід; незалежно від того, чи погано це чи ні. Моя найбільша яловичина - це часто буває - це біль виводити стан об'єкта в читаному для людини форматі. Реалізація ToString () часто недостатньо використовується, і читання стану об'єкта поза списками налагодження налагодження - це біль. Серіалізовані уявлення хороші як резервне копіювання для відображення об'єктів у цілому стані, але кращі варіанти ToString - найкращий варіант.
Еван Плейс

Якщо ToString()призначений для налагодження, то чому це єдиний спосіб витягти весь текст із StringBuilder - важлива функція?
Dan W

9

Мова йде про хорошу практику, як ні про що, насправді.

ToString()використовується в багатьох місцях для повернення рядкового зображення об'єкта, як правило, для споживання людиною. Часто той же рядок може бути використана для регідратації об'єкта (згадайте intабо DateTime, наприклад), але це не завжди даються (дерево, наприклад, може мати корисне враження рядки , яка просто відображає граф, але очевидно , ви не можете використовувати що відбудувати його).

Зокрема, налагоджувач використовуватиме це для відображення змінної у вікнах годин, безпосередніх вікон тощо, тому ToStringнасправді є безцінним для налагодження.

Загалом, також такий тип часто матиме явний член, який також повертає рядок. Наприклад, пара Lat / Long може мати, ToDecimalDegreesяка повертається, "-10, 50"наприклад, але може також мати ToDegreesMinutesSeconds, оскільки це ще один формат для пари Lat / Long. Цей же тип може потім перекрити ToStringодин з тих, щоб надати "за замовчуванням" для таких речей, як налагодження, або навіть потенційно для таких речей, як візуалізація веб-сторінок (наприклад, @конструкція в Razor записує ToString()результат нерядкового виразу в вихідний потік).


6

object.ToString()перетворює об'єкт у його рядкове подання. Якщо ви хочете змінити те, що повертається, коли користувач дзвонить ToString()до створеного вами класу, тоді вам потрібно буде перекрити ToString()цей клас.


6

Хоча я вважаю, що найкорисніша інформація вже надана, я додам свої два центи:

  • ToString()покликаний бути відмінений. Її реалізація за замовчуванням повертає назву типу, яка, хоча може бути корисною часом (особливо при роботі з великою кількістю об'єктів), не є достатньою у більшості разів.

  • Пам’ятайте, що для цілей налагодження можна покластися DebuggerDisplayAttribute. Більше про це можна прочитати тут .

  • Як правило, на POCO завжди можна перекрити ToString(). POCO - це структуроване представлення даних, яке зазвичай може стати рядком.

  • Дизайн ToString - це текстове зображення вашого об'єкта. Можливо його основні поля та дані, можливо опис того, скільки предметів є у колекції тощо.

  • Завжди намагайтеся вписати цей рядок в один рядок і мати лише необхідну інформацію. Якщо у вас є Personклас із властивостями Ім'я, Адреса, Номер тощо, поверніть лише основні дані (Назвіть деякий ідентифікаційний номер).

  • Будьте обережні, щоб не перекрити хорошу реалізацію ToString(). Деякі рамкові класи вже впроваджені ToString(). Перевірка того, що реалізація за замовчуванням - це погано: люди очікують певного результату ToString()і отримають інший.

Не бійтеся використовувати ToString(). Єдине, на що я буду обережним - це повернення конфіденційної інформації. Крім цього, ризик мінімальний. Впевнені, як деякі зазначали, інші класи використовуватимуть ваш ToString кожного разу, коли звертаються до інформації. Але чорт, коли повернення імені типу буде вважатися кращим, ніж отримання деякої фактичної інформації?


5

Якщо ви не переосмислюєте, ToStringтоді ви отримуєте свою базову реалізацію класів, яка, наприклад, Objectє лише короткою назвою класу.

Якщо ви хочете отримати якусь іншу, більш змістовну чи корисну реалізацію, ToStringтоді переосминіть її.


Це може бути корисно при використанні списку вашого типу як джерела даних для а, ListBoxяк ToStringзаповіт, автоматично відображатиметься.

Інша ситуація виникає, коли ви хочете передати свій тип, String.Formatякий викликає, ToStringщоб отримати представлення вашого типу.


4

Щось ще ніхто не згадував: Переосмисливши ToString(), ви також можете розглянути можливість впровадження, IFormattableщоб зробити наступне:

 public override ToString() {
   return string.Format("{0,-20} {1, -20}", m_work, m_personal);
 }

 public ToString(string formatter) {
   string formattedEmail = this.ToString();
   switch (formatter.ToLower()) {
     case "w":
       formattedEmail = m_Work;
       break;
     case "p":
       formattedEmail = m_Personal;
       break;
     case "hw":
       formattedEmail = string.Format("mailto:{0}", m_Work);
       break;
   }
   return formattedEmail;
}

Що може бути корисним.


Чи можете ви надати приклад класу framwork, що надає цей метод для перевизначення? Єдиний супутній матеріал, про який я знаю в рамках, - це IFormattibleінтерфейс.
tm1

@ tm1 Будь-який об’єкт, який успадковує System.Object, матиме ToString()метод перезапису , але ви маєте рацію, що метод форматування не повинен бути overrideі повинен посилатися на IFormattibleінтерфейс для повної відповідності.
Джаф - Бен

Майже - IFormattableприймає IFormatProviderпараметр. Завдяки редагуванню вашої публікації, проте.
tm1

Дійсно, у нього є два способи, значення, яке я висловлював, було в тому, що було показано;)
Джаф - Бен

3

Однією з переваг переосмислення ToString () є підтримка інструментів Resharper: Alt + Ins -> "Форматування членів", і він пише ToString () для вас.


2

У деяких випадках це полегшує читання значень спеціальних класів у вікні перегляду відладчика. Якщо я точно знаю, що хочу бачити у вікні годин, коли я перекриваю ToString з цією інформацією, то я бачу її.


1

При визначенні структур (ефективно для користувача примітиви) Я вважаю , що це хороша практика , щоб мати узгодження ToString, а Parseй TryParseметоди, в зокрема , для XML серіалізациі. У цьому випадку ви перетворите весь стан у рядок, щоб його можна було прочитати згодом.

Класи, однак, є більш складними структурами, які зазвичай є занадто складними для використання ToStringта Parse. Їх ToStringметоди замість збереження всього стану можуть бути простим описом, який допоможе вам ідентифікувати їх стан, як унікальний ідентифікатор, наприклад, ім’я чи ідентифікатор, або, можливо, кількість для списку.

Крім того, як сказав Роббі, переосмислення ToStringдозволяє зателефонувати ToStringза посиланням як базовим як тип object.


1

Ви можете використовувати це, коли у вас є об'єкт з не інтуїтивним значенням представлення рядків, як, наприклад, людина. Отже, якщо вам потрібно, наприклад, для друку цієї людини, ви можете скористатися цією заміною для підготовки її формату.


1

The Object.ToStringМетод повинен використовуватися тільки для налагодження. Реалізація за замовчуванням показує ім'я типу об'єкта, що не дуже корисно. Розглянемо цей спосіб заміни, щоб забезпечити кращу інформацію для діагностики та налагодження. Зверніть увагу, що інфраструктури ведення журналів часто використовують також метод ToString, і ви знайдете ці фрагменти тексту у своїх файлах журналу.

Не повертайте локалізовані текстові ресурси в межах Object.ToStringметоду. Причина полягає в тому, що метод ToString завжди повинен повертати те, що розробник може зрозуміти. Розробник може розмовляти не всіма мовами, які підтримує програма.

Реалізувати на IFormattableінтерфейс , якщо ви хочете , щоб повернути зручний локалізований текст. Цей інтерфейс визначає перевантаження ToString з параметрами formatта formatProvider. FormatProvider допомагає вам форматувати текст у розумінні культури.

Дивіться також: Object.ToString та IFormattable


0

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

Однак виявляється, що ви переосмислюєте метод ToString, щоб не повертати звичайні рядкові дані для вашого ресурсу, а виконати стандартну схему форматування. Оскільки ви використовуєте string.format з підкладкою.

Оскільки ви сказали, що ви навчаєтеся, вправа, здається, також впливає на основні принципи об'єктно-орієнтованого програмування, що стосується інкапсуляції та повторного використання коду.

String.format з аргументами, встановленими для padding, гарантує, що властивість буде форматовано однаково кожного разу для будь-якого коду, який його викликає. Крім того, рухаючись вперед, вам доведеться лише змінити його в одному місці замість багатьох.

Відмінне запитання, а також кілька чудових відповідей!


0

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

Але погоджуючись з тим, що було сказано раніше, щоб дати людині зрозуміле уявлення про предмет, про який йде мова.


0

Я задоволений Рамковими рекомендаціями, згаданими в інших відповідях. Однак я хотів би підкреслити цілі відображення та налагодження.

Будьте уважні до того, як ви використовуєте ToStringу своєму коді. Ваш код не повинен покладатися на строкове представлення об'єкта. Якщо це так, вам слід абсолютно надати відповідні Parseметоди.

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

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