views:

421

answers:

5

The problems are:

  • GUI libraries like to use ToString as a default representation for classes. There it needs to be localized.
  • ToString is used for logging. There it should provide programming related information, is not translated and includes internal states like surrogate keys and enum values.
  • ToString is used by many string operations which take objects as arguments, for instance String.Format, when writing to streams. Depending on the context you expect something different.
  • ToString is too limited if there are many different representations of the same object, eg. a long and a short form.

Because of the different usages, there are many different kinds of implementation. So they are too unreliable to be really useful.

How should ToString be implemented to be useful? When should ToString be used, when should it be avoided?


The .NET Framework documentation says:

This method returns a human-readable string that is culture-sensitive.

There is a similar question, but not the same.

+1  A: 

It depends on the indended usage of your class. Many classes don't have a natural string representation (i.e. a Form object). Then I would implement ToString as an informative method (Form text, size, and so on) useful when debugging. If the class is meant to give information to the user then I would implement ToString as a default representation of the value. If you have a Vector object for instance, then ToString might return the vector as an X and Y coordinate. Here I would also add alternative methods if there are other ways to describe the class. So for the Vector I might add a method that returns a description as an angle and a lenght.

For debugging purposes you may also want to add the DebuggerDisplay attribute to your class. This tells how to display the class in the debugger, but it doesn't affect the string representation.

You may also want to consider making the value returned by ToString to be parseable so that you can create an object from a string representation. Like you can do with the Int32.Parse method.

Rune Grimstad
A: 

Personally, I don't implement ToString that often. In many cases, it wouldn't make a whole lot of sense, since a type's main role may be to define behavior, not data. In other cases, it simply doesn't matter because no clients ever need it.

In any case, here are some cases where it makes sense (not an exhaustive list):

  • If the result of ToString could conceivable be parsed back into an instance of the type without data loss.
  • When the type has a simple (i.e. not complex) value.
  • When the main purpose of the type is to format data into text.

I don't agree that there is a conflict between the usage scenarios that you list. When display is the main purpose, ToString should provide a user-friendly text, but for logging (or rather, as you describe it, for tracing) I would say that you shouldn't be tracing a UI-specific element in any case, but rather an object whose purpose is to write detailed trace data.

So there is no conflict because it should not be the same type according to the Single Responsibility Principle.

Remember that you can always overload the ToString method if you need more control.

Mark Seemann
The problem with "no client ever need it" is, that ToString is called implicitly.The conflict arises when the same object is used in different situations. Is DateTime a UI-specific class? Many classes are not designed to be used only for UI or only internally."You can always overload the ToString": That's the question: HOW should it be implemented then?
Stefan Steinegger
If you use the same object in vastly different situations I believe you are violating the SRP... No, DateTime is not a particularly UI-specific class, but it falls into the category of being able to round-trip ToString/Parse without data loss.
Mark Seemann
+2  A: 

Here is a nice article which explains Overriding System.Object.ToString() and Implementing IFormattable

Sauron
+3  A: 

It seems you have great expectations from a tiny little method :) As far as I know it's not a good idea to use a general method in so many different contexts specially when its behavior can differ from class to class.

Here is my suggestions:

1.Do not let GUI libraries use ToString() of your objects.Instead use more meaningful properties (Almost all controls can be customized to show other properties than ToString) for example use DisplayMember. 2.When getting some information about an object (for logging or other usages) let somebody decide (another object or the object itself)what should be provided and how it should be displayed.(A strategy pattern may come in handy)

Beatles1692
I wouldn't expect that much if it wouldn't be called implicitly all over the place. But you're probably right. I should avoid that it is implicitly used in the ui.
Stefan Steinegger
A: 

Another wrinkle to consider is the tight integration between ToString and Visual Studio's debugger. The Watch window displays the result of ToString as the value of the expression, so if your method performs any lazy-loading, has any side-effects, or takes a long time, then you may see strange behavior or the debugger may appear to hang. Granted, these qualities are not the mark of a well designed ToString method, but they happen (e.g. a naive "fetch the translation from the database" implementation).

Consequently, I consider the default ToString method (without parameters) to be a Visual Studio debugging hook -- with the implication that it should not generally be overloaded for use by the program outside of a debugging context.

While those in the know leverage the debugging attributes (DebuggerTypeProxyAttribute, DebuggerDisplayAttribute, DebuggerBrowsableAttribute) to customize the debugger, many (including myself) generally consider the default output as generated by ToString and displayed in the Watch windows to be good enough.

I understand that this is a rather strict perspective -- writing off ToString as a debugger hook -- but I find that implementing IFormattable seems to be the more reliable and extensible route.

Dave