Почему действительно возражает. ToString () существуют?

В целом это означает поля члена парламента, не занимающего официального поста.

12
задан Silver Phoenix 4 April 2016 в 14:17
поделиться

12 ответов

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

  • ToString ()
  • GetHashCode ()
  • Equals ()

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

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

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

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

подробнее

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

20
ответ дан 2 December 2019 в 03:18
поделиться

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

3
ответ дан 2 December 2019 в 03:18
поделиться

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

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

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

19
ответ дан 2 December 2019 в 03:18
поделиться

Вовсе нет.

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

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

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

3
ответ дан 2 December 2019 в 03:18
поделиться

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

1
ответ дан 2 December 2019 в 03:18
поделиться

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

1
ответ дан 2 December 2019 в 03:18
поделиться

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

0
ответ дан 2 December 2019 в 03:18
поделиться

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

0
ответ дан 2 December 2019 в 03:18
поделиться

И в структурах, и в объектах есть член ToString () для облегчения отладки.

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

0
ответ дан 2 December 2019 в 03:18
поделиться

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

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();
    }
}
0
ответ дан 2 December 2019 в 03:18
поделиться

Я хотел бы добавить пару мыслей о том, почему определение класса .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 (), который имеет поведение базового класса и возвращает тип класса в виде «читаемой человеком» строки.

-1
ответ дан 2 December 2019 в 03:18
поделиться

На самом деле мало смысла возвращать вам Type.FullName , но было бы еще меньше пользы, если бы возвращалась пустая строка или null. Вы спросите , почему он существует. На это не так-то просто ответить, и этот вопрос много лет обсуждался. Более десяти лет назад несколько новых языков решили, что было бы удобно неявно преобразовывать объект в строку, когда это необходимо, эти языки включают 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 полностью отказались от null, как это сделал Бертран Мейер.

Заключение

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

- Абель -

РЕДАКТИРОВАТЬ: спрашивающий отредактировал вопрос и провел сравнение с 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 () , оператор ?? ) созданы для удобства. Ни один из них не является необходимым, но мы уверены, что они у нас есть. Только когда мы знаем, как их правильно использовать, мы также находим истинное значение функции (или нет).

0
ответ дан 2 December 2019 в 03:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: