Why does object.ToString() exist?

Isn't it much more elegant and neat to have an IStringable interface?

Who needs this Type.FullName object returned to us?

EDIT: everyone keeps asking why do I think it's more elegant..

Well, it's just like that, instead of IComparable, object would have CompareTo method, that by default throws an exception or returns 0.

There are objects that cannot and should not be described as a string. object could have equally returned string.Empty. Type.FullName is just an arbitrary choice..

And for methods such as Console.Write(object), I think it should be: Write(IStringable).

However, if you are using WriteLine to anything but strings (or something that its ToString is obvious such as numbers), it seems to me it's for debugging mode only..

By the way - how should I comment to you all? Is it okay that I post an answer?

13.10.2009 17:07:35
So whats the advantage you gain by replacing object.ToString with IStringable { string ToString(); }?
Juliet 13.10.2009 17:10:01
@"Why does object.ToString() exist?" -- pretty sure all objects in .NET support .ToString() by design. Microsoft had a method to their madness.
Hardryv 13.10.2009 17:26:35
+1 and Amen, and at least 15 characters.
csharptest.net 13.10.2009 17:42:44
@Bemrose: It's indeed from Java, see my little history below (or ask Hejlsberg). But if you ask why does ToString() exist, you should also ask why does GetType() exist, we already have typeof operator. And why does Equal() exist, we already have the == operator. There have been very distinct reasons for having ToString (and others) in the base: earlier or later you need to display it one way or the other. So each object simply needs ToString(). As a bonus: you can always create IStringable and use that instead! Ah, and why does C# exist, btw?
Abel 14.10.2009 10:25:05
@Abel Citation needed for "There have been very distinct reasons for having ToString (and others) in the base"
Emperor Orionii 11.01.2013 21:18:01
12 ОТВЕТОВ
РЕШЕНИЕ

There are three virtual methods that IMHO should have never been added to System.Object...

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

All of these could have been implemented as you suggest with an interface. Had they done so I think we'd be much better off. So why are these a problem? Let's just focus on ToString():

  1. If ToString() is expected to be implemented by someone using ToString() and displaying the results you have an implicit contract that the compiler cannot enforce. You assume that ToString() is overloaded, but there is no way to force that to be the case.
  2. With an IStringable you would only need to add that to your generic type-constraint or derive your interface from it to require it's usage on implementing objects.
  3. If the benefit you find in overloading ToString() is for the debugger, you should start using [System.Diagnostics.DebuggerDisplayAttribute].
  4. As for needing this implementation for converting objects to strings via String.Format(), and/or Console.WriteLine, they could have deferred to the System.Convert.ToString(object) and checked for something like 'IStringable', failing over to the type's name if not implemented.
  5. As Christopher Estep points out, it's culture specific.

So I guess I stand alone here saying I hate System.Object and all of it's virtual methods. But I do love C# as a whole and overall I think the designers did a great job.

Note: If you intend to depend upon the behavior of ToString() being overloaded, I would suggest you go ahead and define your IStringable interface. Unfortunatly you'll have to pick another name for the method if you really want to require it.

more

My coworkers and I were just speaking on the topic. I think another big problem with ToString() is answering the question "what is it used for?". Is it Display text? Serialization text? Debugging text? Full type name?

21
13.10.2009 19:15:55
You really adressed the question well and bring a lot of good points, +1!
Meta-Knight 13.10.2009 18:27:07
+1. On point 5, I would say the situation is worse than you've writen. ToString is often culture specific but I don't think you can assume that it always is. Some third party object might use the invariant culture. You just can't rely on ToString for anything other than debugging or logging.
MarkJ 13.10.2009 18:53:33
"the situation is worse than you've writen" no doubt it is. :)
csharptest.net 13.10.2009 19:11:04
Without the equals method you would not be guaranteed to automatically have the equals method when checking strings. GetHashCode makes object comparisons very quick within other structures (this is a good thing). ToStrings make displays of the object within the debugger a lot easier.
monksy 13.10.2009 19:18:36
@mimetnet Java also has toString() so I'm not sure what comparison your talking about. As for other languages in general, I don't have ToString() in C++ and I don't miss it either. I admit I do miss the [DebuggerDisplay] attribute in unmanaged code though :)
csharptest.net 13.10.2009 20:23:26

Having Object.ToString makes APIs like Console.WriteLine possible.

From a design perspective the designers of the BCL felt that the ability to provide a string representation of an instance should be common to all objects. True full type name is not always helpful but they felt the ability to have customizable representation at a root level outweighed the minor annoyance of seeing a full type name in output.

True you could implement Console.WriteLine with no Object.ToString and instead do an interface check and default to the full name of the type if the interface was not present. But then every single API which wanted to capture the string representation of an object instance would have to implement this logic. Given the number of times Object.ToString is used just within the core BCL, this would have lead to a lot of duplication.

19
13.10.2009 17:09:57
It's particularly helpful when examining objects in a debugger or generating exceptions at runtime to be able to emit a textual representation of an object.
LBushkin 13.10.2009 17:15:22
so why isn't Console.WriteLine define as: WriteLine(Istringable)?
Letterman 13.10.2009 17:33:23
I wonder why they made it work for Console.WriteLine but not for Messagebox.Show like they did for VB.NET
Mez 13.10.2009 18:38:33

A "stringable" representation is useful in so many scenarios, the library designers probably thought ToString() was more straightforward.

0
13.10.2009 17:10:00

With IStringable, you will have to do an extra check/cast to see if you can output an object in string format. It's too much of a hit on perf for such a common operation that should be a good thing to have for 99.99% of all objects anyway.

0
13.10.2009 17:10:02
@itay... You are basically advocating a TryCast every time you want to output the value of an object. All of that casting is not necessary, because with .TOString() the instance is guaranteed to produce output.
StingyJack 13.10.2009 18:20:11
Why are you so hot on "for debugging anyway" in all your posts and comments? Yes, it helps with debugging. Yes, it helps with introspection. Debugging constitutes approximately 70-80% of our programming time. Even if I agreed with you, I'd definitely vote for having it. The alternative is that an object can only be represented by its address (as in C++). Luckily, ToString() and its many helpers save not only on debugging but simply makes (parts of) writing code easier: Console.Write, String.Format, ToString(LocaleHere) etc.
Abel 13.10.2009 22:09:28
(you == Itay, not StingyJack, sorry if that was unclear by my wording)
Abel 13.10.2009 22:10:23

I imagine it exists because it's a wildly convenient thing to have on all objects and doesn't require add'l cruft to use. Why do you think IStringable would be more elegant?

3
13.10.2009 17:10:31
because there are things that definitely can be described as strings, and there are things that are not. it's like instead of IComperable, object would had CompareTo, which, as default throw exception.
Letterman 13.10.2009 17:36:10

Not at all.

It doesn't need to be implemented and it returns culture-specific results.

This method returns a human-readable string that is culture-sensitive. For example, for an instance of the Double class whose value is zero, the implementation of Double..::.ToString might return "0.00" or "0,00" depending on the current UI culture.

Further, while it comes with its own implementation, it can be overriden, and often is.

3
13.10.2009 17:10:54

Why make it more complicated? The way it is right now basically establishes that each and every object is capable of printing its value to a string, I can't see anything wrong with that.

1
13.10.2009 17:11:17

Mmmm, so it can be overridden in derived classes possibly?

1
13.10.2009 17:14:00

Structs and Objects both have the ToString() member to ease debugging.

The easiest example of this can be seen with Console.WriteLine which receives a whole list of types including object, but also receives params object[] args. As Console is often a layer on-top of TextWriter these statements are also helpful (sometimes) when writing to files and other streams (sockets).

It also illustrates a simple object oriented design that shows you interfaces shouldn't be created just because you can.

0
13.10.2009 17:18:01
Ever heard of the DebuggerDisplayAttribute?
Meta-Knight 13.10.2009 19:16:34
I'm not a fan of debuggers for anything other than seg-faults in C/C++. I personally feel as though they hurt your long term ability more than they can help with the short term fix.
mimetnet 13.10.2009 19:51:46
DebuggerDisplayAttribute was not added until .Net 2.0.
Preston McCormick 15.05.2012 21:13:10
From a pure design standpoint, ToString() is not obviously related to the debugger (or if you want a less theoretical example, in the codebase I'm working on, the last set of programmers assumed that ToString() was something they should feel free to do a time consuming operation on). From a non-debugging standpoint, I've grown to dislike classes that take in any object and automatically assume that any override of ToString() will be the right format, if anything, I want the compiler to error out if it's just going to print out the fully qualified typename instead of useful data.
jrh 8.01.2018 20:48:12
... Also ToString could mean almost anything, the fully qualified typename (default behavior), debugging text (common usage), localized display text (arguably the most practical behavior), the binary data of the object converted to a Base64 string, the data of the object in csv format, etc. etc. -- and I've seen all of these in various codebases. Sure, you can add a comment saying what exactly it does, and MS can document its typical usage, but neither of those would be necessary if .NET didn't offer this method to begin with as something that all objects have.
jrh 8.01.2018 20:49:31

I'd like to add a couple of thoughts on why .NET's System.Object class definition has a ToString() method or member function, in addition to the previous postings on debugging.

Since the .NET Common Language Runtime (CLR) or Execution Runtime supports Reflection, being able to instantiate an object given the string representation of the class type seems to be essential and fundamental. And if I'm not mistaken, all reference values in the CLR are derived from System.Object, having the ToString() method in the class ensures its availability and usage through Reflection. Defining and implementing an interface along the lines of IStringable, is not mandatory or required when defining a class in .NET, and would not ensure the ability to dynamically create a new instance after querying an assembly for its supported class types.

As more advanced .NET functionality available in the 2.0, 3.0 and 3.5 runtimes, such as Generics and LINQ, are based on Reflection and dynamic instantiation, not to mention .NET's Dynamic Language Runtime (DLR) support that allow for .NET implementations of scripting languages, such as Ruby and Python, being able to identify and create an instance by a string type seems to be an essential and indispensable function to have in all class definitions.

In short, if we can't identify and name a specific class we want to instantiate, how can we create it? Relying on a ToString() method that has the base class behavior of returning the Class Type as a "human readable" string seems to make sense.

Maybe a review of the articles and books from Jeffrey Ricther and Don Box on the .NET Framework design and architecture may provide better insights on this topic as well.

-1
13.10.2009 18:15:08
1. "In short, if we can't identify and name a specific class we want to instantiate, how can we create it?" You can via Object.GetType().FullName. 2. "... to instantiate an object given the string representation of the class type..." That is just the problem with ToString(), it has no meaning because it's virtual. It can be anything or null and it is not a substitute for calling Object.GetType().FullName.
csharptest.net 13.10.2009 18:27:47

My new base class:

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
13.10.2009 18:42:40
What, pray tell, is the problem that you are intending to solve?
Robert Rossney 13.10.2009 19:19:26
All of these methods in derived implementations now have a dependable behavior. Now I can know what ToString() is use for, and what Equals means.
csharptest.net 13.10.2009 19:39:25
and you limit the possibility to derive from MarhalByRefObject, Page, Form or other important classes when you need this. Unfortunately, multiple inheritance is not supported in C# and changing the common base class is not possible. Nice idea, but can unfortunately not be used in practice.
Abel 13.10.2009 22:13:47
@Abel LOL, true enough on the MBR etc. Obviously I'm being a bit sarcastic with the above class to overstate the point.
csharptest.net 13.10.2009 22:25:26
@csharptest, aha, glad you say that, you got me worried for a moment, lol!
Abel 14.10.2009 10:14:40

There's indeed little use of having the Type.FullName returned to you, but it would be even less use if an empty string or null were returned. You ask why it exists. That's not too easy to answer and has been a much debated issue for years. More then a decade ago, several new languages decided that it would be convenient to implicitly cast an object to a string when it was needed, those languages include Perl, PHP and JavaScript, but none of them is following the object orientation paradigm thoroughly.

Approaches

Designers of object oriented languages had a harder problem. In general, there were three approaches for getting the string representation of an object:

  • Use multiple inheritance, simply inherit from String as well and you can be cast to a string
  • Single inheritance: add ToString to the base class as a virtual method
  • Either: make the cast operator or copy constructor overloadable for strings

Perhaps you'd ask yourself Why would you need a ToString or equiv. in the first place? As some others already noted: the ToString is necessary for introspection (it is called when you hover your mouse over any instance of an object) and the debugger will show it too. As a programmer, you know that on any non-null object you can safely call ToString, always. No cast needed, no conversion needed.

It is considered good programming practice to always implement ToString in your own objects with a meaningful value from your persistable properties. Overloads can help if you need different types of representation of your class.

More history

If you dive a bit deeper in the history, we see SmallTalk taking a wider approach. The base object has many more methods, including printString, printOn etc.

A small decade later, when Bertrand Meyer wrote his landmark book Object Oriented Software construction, he suggested to use a rather wide base class, GENERAL. It includes methods like print, print_line and tagged_out, the latter showing all properties of the object, but no default ToString. But he suggests that the "second base object ANY to which all user defined object derive, can be expanded", which seems like the prototype approach we now know from JavaScript.

In C++, the only multiple inheritance language still in widespread use, no common ancestor exists for all classes. This could be the best candidate language to employ your own approach, i.e. use IStringable. But C++ has other ways: you can overload the cast operator and the copy constructor to implement stringability. In practice, having to be explicit about a to-string-implementation (as you suggest with IStringable) becomes quite cumbersome. C++ programmers know that.

In Java we find the first appearance of toString for a mainstream language. Unfortunately, Java has two main types: objects and value types. Value types do not have a toString method, instead you need to use Integer.toString or cast to the object counterpart. This has proven very cumbersome throughout the years, but Java programmers (incl. me) learnt to live with it.

Then came C# (I skipped a few languages, don't want to make it too long), which was first intended as a display language for the .NET platform, but proved very popular after initial skepticism. The C# designers (Anders Hejlsberg et al) looked mainly at C++ and Java and tried to take the best of both worlds. The value type remained, but boxing was introduced. This made it possible to have value types derive from Object implicitly. Adding ToString analogous to Java was just a small step and was done to ease the transition from the Java world, but has shown its invaluable merits by now.

Oddity

Though you don't directly ask about it, but why would the following have to fail?

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

and while you think about it, consider the following, which does not fail:

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

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

which makes me ask the question: would, if the language designers had thought of extension methods early on, the ToString method be part of the extension methods to prevent unnecessary NullPointerExceptions? Some consider this bad design, other consider it a timesaver.

Eiffel, at the time, had a special class NIL which represented nothingness, but still had all the base class's methods. Sometimes I wished that C# or Java had abandoned null altogether, just like Bertrand Meyer did.

Conclusion

The wide approach of classical languages like Eiffel and Smalltalk has been replaced by a very narrow approach. Java still has a lot of methods on Object, C# only has a handful. This is of course good for implementations. Keeping ToString in the package simply keeps programming clean and understandable at the same time and because it is virtual, you can (and should!) always override it, which will make your code better apprehendable.

-- Abel --

EDIT: the asker edited the question and made a comparison to IComparable, same is probably true for ICloneable. Those are very good remarks and it is often considered that IComparable should've been included in Object. In line with Java, C# has Equals and not IComparable, but against Java, C# does not have ICloneable (Java has clone()).

You also state that it is handy for debugging only. Well, consider this everywhere you need to get the string version of something (contrived, no ext. methods, no String.Format, but you get the idea):

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");

and compare that with this. Whichever you find easier you should choose:

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

Remember that languages are about making things clearer and easier. Many parts of the language (LINQ, extension methods, ToString(), the ?? operator) are created as conveniences. None of these are necessities, but sure are we glad that we have them. Only when we know how to use them well, we also find the true value of a feature (or not).

0
13.10.2009 19:42:34
huh? so with the object's ToString you will something like: Car OuterNameSpace.InternalNamespace.CarInfo, Bike OuterNameSpace.InternalNamespace.BikeInfo. yeah, real user friendly.. anyway, you miss the point, where, ever, outside of debugging, will you need will you need to convert something to string, without knowing whether it support it or not? and if it's not, what will you show your user?
Letterman 13.10.2009 20:51:56
@Abel: that was quite a long time ago and actually i took me sometime to understand what i've meant. about your last example, how possibly will you not know weather or not a specific type implements IStringable or not? but even if you get an instance as an argument, and you don't know what it is, is STILL doesn't make sense to do what you did, because if it does not override ToString you will get the full type name which is certainly not something to send to the user.. and you can not GUARANTEE it's overridden which is important. however
Letterman 7.02.2011 16:42:04
and however, if you want to get an object that can be represented as text, just get an IStringable. i don't get how it get so complicated...... object.ToString() is for everything and IStringable.ToString() is only for the stringables. end of story. i still say, where, ever, outside of debugging will you want to string something without knowing what weather it's implemented or not.... and in practice, A LOT of objects do not implement ToString
Letterman 7.02.2011 16:52:16
Having MemberwiseClone as a protected function of Object which defaults to being available in all subclasses was a mistake (unlike ToString()). It would have been better to either have a "magic" Cloneable derived from Object, which included a protected virtual BaseClone method, and have things which should support any type of cloning derive from that, or else have some form of "opt-in" protected members such that if a class doesn't ask for them, its subclasses can't either. There are many classes where it is impossible for a subclass to use MemberwiseClone legitimately.
supercat 19.12.2013 07:36:14
Taking that concept a little further, I would have allowed for boxing of not just value types, but any type. Basically, a boxed T would be like a T[], but without any dimension. Just as an Animal[1] which holds an reference to a SiameseCat is distinguishable from a Cat[1] which holds a reference to that same instance, so too I would like to see box types. The fiction that value types and class types are all Object is IMHO more confusing than helpful.
supercat 19.12.2013 07:49:48