Действительно, возвращать Type.FullName
вам мало смысла, но было бы еще менее полезно, если бы возвращалась пустая строка или ноль. Вы спрашиваете , почему существует. На этот вопрос не так-то просто ответить, и эта проблема обсуждалась годами. Более десяти лет назад несколько новых языков решили, что было бы удобно неявно приводить объект к строке, когда это необходимо, эти языки включают в себя Perl, PHP и JavaScript, но ни один из них не следует полностью парадигме ориентации объекта.
Подходы
У разработчиков объектно-ориентированных языков возникла более сложная проблема. В целом, было три подхода для получения строкового представления объекта:
- Используйте множественное наследование, просто наследуйте от
String
, и вы можете быть преобразованы в строку
- Одиночное наследование: добавьте
ToString
к базовому классу как виртуальный метод
- Либо: сделать оператор приведения или конструктор копирования перегружаемым для строк
Возможно, вы спросите себя, зачем вам нужен ToString
или эквивалент? на первом месте? Как уже отмечали некоторые другие: ToString
необходим для самоанализа (он вызывается, когда вы наводите указатель мыши на какой-либо объект), и отладчик будет показывать это тоже. Как программист, вы знаете, что для любого ненулевого объекта вы всегда можете безопасно вызывать ToString
. Не требуется каст, конверсия не требуется.
Хорошей практикой программирования считается всегда реализация ToString
в ваших собственных объектах с осмысленным значением из ваших постоянных свойств. Перегрузки могут помочь, если вам нужны разные типы представлений вашего класса.
Больше истории
Если вы погрузитесь немного глубже в историю, мы увидим, что SmallTalk использует более широкий подход. Базовый объект имеет много других методов, включая printString
, printOn
и т. Д.
Небольшое десятилетие спустя, когда Бертран Мейер написал свою выдающуюся книгу «Построение объектно-ориентированного программного обеспечения», он предложил использовать довольно широкий базовый класс, GENERAL
. Он включает в себя такие методы, как print
, print_line
и tagged_out
, причем последний показывает все свойства объекта, но не имеет значения по умолчанию ToString
. Но он предполагает, что "второй базовый объект, ЛЮБОЙ, к которому ведут все определенные пользователем объекты, может быть расширен" , что похоже на прототип, который мы теперь знаем из JavaScript.
В C ++, единственном языке множественного наследования, все еще широко распространенном, общего предка не существует для всех классов. Это может быть лучшим языком кандидата для использования вашего собственного подхода, то есть используйте IStringable
. Но у C ++ есть и другие способы: вы можете перегрузить оператор приведения и конструктор копирования, чтобы реализовать реализуемость. На практике необходимость явно указывать на реализацию в строку (как вы предлагаете с IStringable
) становится довольно громоздкой. Программисты C ++ знают это.
В Java мы находим первое появление toString
для основного языка. К сожалению, у Java есть два основных типа: объекты и типы значений. Типы значений не имеют метода toString
, вместо этого вам нужно использовать Integer.toString
или привести к объектному аналогу. На протяжении многих лет это оказалось очень громоздким, но программисты на Java (включая меня) научились жить с этим.
Затем появился C # (я пропустил несколько языков, не хочу делать его слишком длинным), который сначала был задуман как язык отображения для платформы .NET, но оказался очень популярным после первоначального скептицизма. Дизайнеры C # (Андерс Хейлсберг и др.) Смотрели в основном на C ++ и Java и пытались взять лучшее из обоих миров. Тип значения остался, но был введен бокс. Это позволило неявно получать типы значений из Object. Добавление ToString
, аналогичного Java, было небольшим шагом и было сделано для облегчения перехода из мира Java, но уже продемонстрировало его бесценные достоинства.
Oddity
Хотя вы прямо не спрашиваете об этом, но почему следующее должно потерпеть неудачу?
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 был бы частью методов расширения для предотвращения ненужных исключений NullPointerExceptions? Некоторые считают это плохим дизайном, другие считают, что это экономит время.
У Эйфеля в то время был специальный класс NIL
, который представлял собой ничто, но все еще имел все методы базового класса. Иногда мне хотелось, чтобы C # или Java вообще отказались от нуля, как это делал Бертран Мейер.
Заключение
Широкий подход классических языков, таких как 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()
, оператор ??
) создаются как удобства. Ничего из этого не является необходимым, но мы, конечно, рады, что они у нас есть. Только когда мы знаем, как правильно их использовать, мы также находим истинное значение функции (или нет).