Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему существует object.ToString()?

Автор вопроса: LettermanОтветов: 12

Разве интерфейс IStringable не намного элегантнее и изящнее?

Кому нужен этот объект Type.FullName, возвращенный нам?

EDIT: все продолжают спрашивать, почему я думаю, что это более элегантно..

Ну, просто так, вместо IComparable у объекта будет метод CompareTo, который по умолчанию выдает исключение или возвращает 0.

Есть объекты, которые не могут и не должны описываться строкой. объект мог бы точно так же вернуть string.Empty. Type.FullName это просто произвольный выбор..

А для таких методов, как Console.Write(object), я думаю, должно быть: Write(IStringable).

Однако, если вы используете WriteLine для чего-либо, кроме строк (или чего-то, что его ToString очевидно, например, чисел), мне кажется, что это только для режима отладки.

Кстати, как мне вам всем прокомментировать? Ничего, что я опубликую ответ?

  • Итак, в чем преимущество, которое вы получите, заменив object.ToString на IStringable { string ToString(); }? Juliet 13.10.2009
  • @Почему существует object.ToString()? -- почти уверен, что все объекты в .NET поддерживают .ToString() по дизайну. У Microsoft был способ их безумия. Hardryv 13.10.2009
  • +1 и Аминь и не менее 15 символов. csharptest.net 13.10.2009
  • Чтобы прокомментировать для всех нас, либо добавьте комментарий к своему вопросу, либо добавьте четко обозначенное обновление к своему вопросу. StriplingWarrior 13.10.2009
  • Я просто предполагаю, что это пережиток Java. Однако Java использует метод toString() всякий раз, когда вы используете оператор + для объектов. В Java это может сойти с рук, потому что она не поддерживает перегрузку операторов. Powerlord 13.10.2009
  • @Bemrose: Это действительно с Java, см. мою небольшую историю ниже (или спросите у Хейлсберга). Но если вы спросите, почему существует ToString(), вы также должны спросить, почему существует GetType(), у нас уже есть оператор typeof. И почему существует Equal(), у нас уже есть оператор ==. Были очень внятные причины для наличия ToString (и других) в базе: раньше или позже нужно отображать так или иначе. Таким образом, каждому объекту просто требуется ToString(). В качестве бонуса: вы всегда можете создать IStringable и использовать его вместо него! А, кстати, зачем вообще существует C#? Abel 14.10.2009

object

Ответы

Ответ 1 · рейтинг 22 · csharptest.net

Есть три виртуальных метода, которые ИМХО никогда не следовало добавлять в System.Object...

  • Нанизывать()
  • ПолучитьHashCode()
  • Равно()

Все это можно было бы реализовать, как вы предлагаете, с интерфейсом. Если бы они так поступили, я думаю, нам было бы намного лучше. Так почему это проблема? Давайте просто сосредоточимся на ToString():

  1. Если ожидается, что ToString() будет реализован кем-то, использующим ToString() и отображающим результаты, у вас есть неявный контракт, который компилятор не может обеспечить. Вы предполагаете, что ToString() перегружена, но нет способа заставить это быть так.
  2. С IStringable вам нужно будет только добавить это к вашему общему ограничению типа или получить от него свой интерфейс, чтобы потребовать его использования при реализации объектов.
  3. Если преимущество, которое вы обнаружите в перегрузке ToString(), предназначено для отладчика, вы должны начать использовать [System.Diagnostics.DebuggerDisplayAttribute].
  4. Что касается необходимости этой реализации для преобразования объектов в строки через String.Format() и/или Console.WriteLine, они могли бы отложить до System.Convert.ToString(object) и проверить что-то вроде "IStringable", переключившись на имя типа, если оно не реализовано.
  5. Как отмечает Кристофер Эстеп, это зависит от культуры.

Так что я думаю, что я здесь один, говоря, что я ненавижу System.Object и все его виртуальные методы. Но я люблю C# в целом и считаю, что дизайнеры отлично поработали.

Примечание. Если вы намерены зависеть от перегруженного поведения ToString(), я бы посоветовал вам продолжить и определить свой интерфейс IStringable. К сожалению, вам придется выбрать другое имя для метода, если вы действительно хотите его использовать.

больше

Мы с коллегами как раз говорили на эту тему. Я думаю, что еще одна большая проблема с ToString() - это ответ на вопрос "для чего он используется?". Это отображаемый текст? Текст сериализации? Отладка текста? Полное имя типа?

Ответ 2 · рейтинг 20 · JaredPar

Наличие Object.ToString делает возможными такие API, как Console.WriteLine.

С точки зрения дизайна разработчики BCL считали, что возможность предоставления строкового представления экземпляра должна быть общей для всех объектов. Истинное полное имя типа не всегда полезно, но они считают, что возможность иметь настраиваемое представление на корневом уровне перевешивает незначительное раздражение от просмотра полного имени типа в выводе.

Правда, вы можете реализовать Console.WriteLine без Object.ToString и вместо этого выполнить проверку интерфейса и по умолчанию использовать полное имя типа, если интерфейс отсутствует. Но тогда каждый отдельный API, который хотел бы получить строковое представление экземпляра объекта, должен был бы реализовать эту логику. Учитывая, сколько раз Object.ToString используется только в основном BCL, это привело бы к большому дублированию.

Ответ 3 · рейтинг 3 · Greg D

Я предполагаю, что он существует, потому что это чрезвычайно удобно иметь на всех объектах и ​​не требует дополнительных усилий для использования. Как вы думаете, почему IStringable будет более элегантно?

Ответ 4 · рейтинг 3 · Chris E

Нисколько.

Его не нужно реализовывать, и он возвращает результаты, зависящие от языка и региональных параметров.

Этот метод возвращает удобочитаемую строку с учетом языка и региональных параметров. Например, для экземпляра класса Double, значение которого равно нулю, реализация Double..::.ToString может вернуть "0,00" или "0,00" в зависимости от текущего языка и региональных параметров пользовательского интерфейса.

Кроме того, хотя он поставляется со своей собственной реализацией, его можно переопределить, что часто и происходит.

Ответ 5 · рейтинг 1 · Otávio Décio

Зачем усложнять? То, как это происходит сейчас, в основном устанавливает, что каждый объект способен печатать свое значение в строку, я не вижу в этом ничего плохого.

Ответ 6 · Larsenal

"Строковое" представление полезно во многих сценариях, и разработчики библиотеки, вероятно, думали, что ToString() более прямолинеен.

Ответ 7 · Xerion

С IStringable вам придется выполнить дополнительную проверку/приведение, чтобы увидеть, можете ли вы вывести объект в строковом формате. Это слишком большой удар по производительности для такой общей операции, которая в любом случае должна быть хорошей вещью для 99,99% всех объектов.

Ответ 8 · leppie

Мммм, значит, его можно переопределить в производных классах?

Ответ 9 · mimetnet

Структуры и объекты имеют член ToString() для облегчения отладки.

Самый простой пример этого можно увидеть с помощью Console.WriteLine, который получает весь список типов, включая объект, но также получает params object[] args. Поскольку Console часто является слоем поверх TextWriter, эти операторы также полезны (иногда) при записи в файлы и другие потоки (сокеты).

Он также иллюстрирует простой объектно-ориентированный дизайн, который показывает, что интерфейсы не должны создаваться только потому, что вы можете это сделать.

Ответ 10 · csharptest.net

Мой новый базовый класс:

class Object : global::System.Object
{
    [Obsolete("Do not use ToString()", true)]
    public sealed override string ToString()
    {
        return base.ToString();
    }

    [Obsolete("Do not use Equals(object)", true)]
    public sealed override bool Equals(object obj)
    {
        return base.Equals(this, obj);
    }

    [Obsolete("Do not use GetHashCode()", true)]
    public sealed override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

Ответ 11 · Abel

Действительно, мало смысла возвращать вам Type.FullName, но было бы еще меньше пользы, если бы возвращалась пустая строка или ноль. Вы спрашиваете, почему он существует. На этот вопрос не так-то просто ответить, и он является предметом многочисленных дискуссий в течение многих лет. Более десяти лет назад несколько новых языков решили, что было бы удобно неявно приводить объект к строке, когда это необходимо. К таким языкам относятся Perl, PHP и JavaScript, но ни один из них не следует полностью парадигме объектной ориентации.

подходы

Разработчики объектно-ориентированных языков столкнулись с более сложной проблемой. В общем, было три подхода для получения строкового представления объекта:

  • Используйте множественное наследование, просто наследуйте от String, и вы можете быть преобразованы в строку
  • Одиночное наследование: добавьте ToString в базовый класс как виртуальный метод
  • Либо: сделать оператор приведения или конструктор копирования перегружаемым для строк

Возможно, вы спросите себя, зачем вам нужен ToString или аналогичный. в первую очередь? Как уже отмечали некоторые другие: ToString необходим для самоанализа (он вызывается, когда вы наводите указатель мыши на любой экземпляр объекта), и отладчик тоже его покажет. Как программист, вы знаете, что для любого ненулевого объекта вы всегда можете безопасно вызвать ToString. Не нужен литой, не нужна конверсия.

Считается хорошей практикой программирования всегда реализовывать ToString в ваших собственных объектах со значимым значением из ваших сохраняемых свойств. Перегрузки могут помочь, если вам нужны разные типы представления вашего класса.

Больше истории

Если вы углубитесь в историю, мы увидим, что SmallTalk использует более широкий подход. Базовый объект имеет гораздо больше методов, включая printString, printOn и т.д.

Небольшое десятилетие спустя, когда Бертран Мейер написал свою знаменательную книгу "Конструкция объектно-ориентированного программного обеспечения", он предложил использовать довольно широкий базовый класс GENERAL. Он включает такие методы, как print, print_line и tagged_out, причем последний показывает все свойства объекта, но не ToString по умолчанию. Но он предполагает, что "второй базовый объект ANY, от которого происходят все пользовательские объекты, может быть расширен", что похоже на подход прототипа, который мы теперь знаем из JavaScript.

В C++, единственном языке множественного наследования, все еще широко используемом, не существует общего предка для всех классов. Это может быть лучший язык-кандидат для использования вашего собственного подхода, то есть используйте IStringable. Но у C++ есть и другие способы: вы можете перегрузить оператор приведения и конструктор копирования, чтобы реализовать возможность строк. На практике необходимость явно указывать реализацию to-string (как вы предлагаете с IStringable) становится довольно громоздкой. Программисты на С++ это знают.

В Java мы находим первое появление toString для основного языка. К сожалению, в Java есть два основных типа: объекты и типы значений. Типы значений не имеют метода toString, вместо этого вам нужно использовать Integer.toString или привести к аналогу объекта. Это оказалось очень громоздким на протяжении многих лет, но программисты Java (включая меня) научились с этим жить.

Затем появился C# (я пропустил несколько языков, не хочу делать его слишком длинным), который изначально задумывался как язык отображения для платформы .NET, но оказался очень популярным после первоначального скептицизма. Разработчики C# (Андерс Хейлсберг и др.) смотрели в основном на C++ и Java и пытались взять лучшее из обоих миров. Тип значения остался, но был введен бокс. Это позволило неявно наследовать типы значений от Object. Добавление ToString аналога Java было всего лишь небольшим шагом и было сделано для облегчения перехода от мира Java, но к настоящему времени оно показало свои неоценимые достоинства.

странность

Хотя вы прямо не спрашиваете об этом, но почему следующее должно потерпеть неудачу?

object o = null;
Console.WriteLine(o.ToString());

и пока вы думаете об этом, учтите следующее, что не подводит:

public static string MakeString(this object o)
{ return o == null ? "null" : o.ToString();  }

// elsewhere:
object o = null;
Console.WriteLine(o.MakeString());

что заставляет меня задать вопрос: если бы разработчики языка заранее подумали о методах расширения, был бы метод ToString частью методов расширения для предотвращения ненужных исключений NullPointerException? Некоторые считают этот дизайн плохим, другие считают его экономией времени.

У Eiffel в то время был специальный класс NIL, который представлял ничто, но все же имел все методы базового класса. Иногда мне хотелось, чтобы C# или Java вообще отказались от null, как это сделал Бертран Мейер.

Вывод

Широкий подход классических языков, таких как Eiffel и Smalltalk, был заменен очень узким подходом. В Java по-прежнему много методов для Object, а в C# - всего несколько. Это, конечно, хорошо для реализации. Хранение ToString в пакете просто делает программирование чистым и в то же время понятным, а поскольку это virtual, вы можете (и должны!) всегда переопределять его, что сделает ваш код более понятным.

-- Абель --

EDIT: автор вопроса отредактировал вопрос и сравнил его с IComparable, то же самое, вероятно, верно и для ICloneable. Это очень хорошее замечание, и часто считается, что IComparable следовало бы включить в Object. В соответствии с Java, C# имеет Equals, а не IComparable, но в отличие от Java C# не имеет ICloneable (в Java есть clone()).

Вы также заявляете, что это удобно только для отладки. Что ж, учтите это везде, где вам нужно получить строковую версию чего-либо (надуманно, без дополнительных методов, без String.Format, но вы поняли):

CarInfo car = new CarInfo();
BikeInfo bike = new BikeInfo();
string someInfoText = "Car " +
   (car is IStringable) ? ((IStringable) car).ToString() : "none") +
   ", Bike " +
   (bike is IStringable) ? ((IStringable) bike).ToString() : "none");

и сравните это с этим. Вы должны выбрать то, что вам проще:

CarInfo car = new CarInfo();
BikeInfo bike = new BikeInfo();
string someInfoText = "Car " + car.ToString() + ", Bike " + bike.ToString();

Помните, что языки предназначены для того, чтобы делать вещи яснее и проще. Многие части языка (LINQ, методы расширения, ToString(), оператор ??) созданы для удобства. Ничего из этого не является необходимостью, но мы, конечно же, рады, что они у нас есть. Только когда мы знаем, как их правильно использовать, мы также находим истинную ценность функции (или нет).

Ответ 12 · рейтинг -1 · ClockEndGooner

Я хотел бы добавить пару мыслей о том, почему определение класса .NET System.Object имеет метод или функцию-член ToString() в дополнение к предыдущим сообщениям об отладке.

Поскольку .NET Common Language Runtime (CLR) или Execution Runtime поддерживает Reflection, возможность создания экземпляра объекта с учетом строкового представления типа класса кажется важной и фундаментальной. И если я не ошибаюсь, все ссылочные значения в CLR являются производными от System.Object, наличие метода ToString() в классе обеспечивает его доступность и использование через Reflection. Определение и реализация интерфейса наподобие IStringable не является обязательным или обязательным при определении класса в .NET и не гарантирует возможности динамического создания нового экземпляра после запроса сборки на предмет поддерживаемых типов классов.

Поскольку более продвинутые функции .NET, доступные в средах выполнения 2.0, 3.0 и 3.5, такие как Generics и LINQ, основаны на отражении и динамическом создании экземпляров, не говоря уже о поддержке .NET Dynamic Language Runtime (DLR), которая позволяет реализовывать сценарии .NET. В языках, таких как Ruby и Python, возможность идентифицировать и создавать экземпляр по строковому типу кажется существенной и незаменимой функцией, которую необходимо иметь во всех определениях классов.

Короче говоря, если мы не можем определить и назвать конкретный класс, который хотим создать, как мы можем его создать? Кажется, имеет смысл полагаться на метод ToString(), который имеет поведение базового класса, возвращая тип класса в виде "читаемой человеком" строки.

Возможно, обзор статей и книг Джеффри Риктера и Дона Бокса по проектированию и архитектуре .NET Framework также поможет лучше понять эту тему.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы