tags:

views:

563

answers:

12

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

Who needs this Type.FullName object returned to us?

EDIT: everyone keep asking why do i think it's more elegant..

well, it's just like that instead of IComperable, object would had CompereTo method, that by default throwing exception or returning 0.

there are objects that cannot and should not be described as string. object could have equally return string.Empty. Type.FullName is just an arbitrary choose..

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?

+15  A: 

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.

JaredPar
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
so why isn't Console.WriteLine define as:WriteLine(Istringable)?
Itay
I wonder why they made it work for Console.WriteLine but not for Messagebox.Show like they did for VB.NET
Mez
+1  A: 

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

Larsenal
A: 

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.

Xerion
if you are using ToString in such blindness it's probably for debugging anyway..
Itay
@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
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
(you == Itay, not StingyJack, sorry if that was unclear by my wording)
Abel
+4  A: 

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?

Greg D
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.
Itay
+3  A: 

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.

Christopher Estep
+2  A: 

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.

Otávio Décio
+1  A: 

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

leppie
A: 

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.

mimetnet
Ever heard of the DebuggerDisplayAttribute?
Meta-Knight
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
+4  A: 

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?

csharptest.net
You really adressed the question well and bring a lot of good points, +1!
Meta-Knight
+1! damn yes!! love C#, hate system.object..
Itay
+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
"the situation is worse than you've writen" no doubt it is. :)
csharptest.net
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
Too many interfaces are for the Java/Apache frameworks. There's more benefit then pain with the methods you point out.
mimetnet
@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
My most down-voted answer. Some people just don't get it :)
csharptest.net
A: 

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.

ClockEndGooner
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
Point well made and well taken. Thank you....
ClockEndGooner
A: 

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();
 }
}
csharptest.net
What, pray tell, is the problem that you are intending to solve?
Robert Rossney
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
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
@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
@csharptest, aha, glad you say that, you got me worried for a moment, lol!
Abel
A: 

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).

Abel
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?
Itay
No, that's a common misunderstanding. The `ToString` is normally overridden, it's a common design principle for serializable objects (sorry about cars and bikes, consider them normal POCOs or similar) and even non-serializable object should override `ToString`. When you override it, the ToString of *your* object is called. That's why it is a virtual method in the first place. Sorry if my example wasn't clear. There are few situations where you do not ever need ToString() or ToString(params), but I can't judge your situation. No POCO should be without it. It just saves time and adds clarity.
Abel
btw, what did you mean with, quote: _"Bike OuterNameSpace.InternalNamespace.BikeInfo"_ as there's no need to specify the namespaces. You import that with the `using` directives, same as in other languages.
Abel