为什么/什么时候覆盖 ToString 是合适的?

我正在学习 C # ,我想知道覆盖 ToString的意义和好处是什么,如下面的例子所示。

是否可以用一种更简单的方法,使用一种没有重写的通用方法来完成这项工作?

public string GetToStringItemsHeadings
{
get { return string.Format("{0,-20} {1, -20}", "Office Email", "Private Email"); }
}




public override string ToString()
{
string strOut = string.Format("{0,-20} {1, -20}", m_work, m_personal);
return strOut;
}
25282 次浏览

Overriding ToString() allows you to give a useful human-readable string representation of a class.

This means that the output can reveal useful information about your class. For example, if you had a Person class you might choose to have the ToString() output the person's id, their firstname, their lastname etc. This is extremely useful when debugging or logging.

With regard to your example - it is difficult to tell if your override is useful without knowing what this class is - but the implementation itself is ok.

If you don't override ToString then you get your base classes implementation which, for Object is just the short type name of the class.

If you want some other, more meaningful or useful implementation of ToString then override it.


This can be useful when using a list of your type as the datasource for a ListBox as the ToString will be automatically displayed.

Another situtation occurs when you want to pass your type to String.Format which invokes ToString to get a representation of your type.

  • Do you need to override ToString? No.

  • Can you get a string representation of your object in another way? Yes.

But by using ToString you are using a method that is common to all objects and thus other classes know about this method. For instance, whenever the .NET framework wants to convert an object to a string representation, ToString is a prime candidate (there are others, if you want to provide more elaborate formatting options).

Concretely,

Console.WriteLine(yourObject);

would invoke yourObject.ToString().

object.ToString() converts an object to its string representation. If you want to change what is returned when a user calls ToString() on a class you have created then you would need to override ToString() in that class.

It's about good practise as much as anything, really.

ToString() is used in many places to return a string representation of an object, generally for consumption by a human. Often that same string can be used to rehydrate the object (think of int or DateTime for example), but that's not always a given (a tree for example, might have a useful string representation which simply displays a Count, but clearly you can't use that to rebuild it).

In particular the debugger will use this to display the variable in the watch windows, immediate windows etc, therefore ToString is actually invaluable for debugging.

In general, also, such a type will often have an explicit member that returns a string as well. For example a Lat/Long pair might have ToDecimalDegrees which returns "-10, 50" for example, but it might also have ToDegreesMinutesSeconds, since that is another format for a Lat/Long pair. That same type might then also override ToString with one of those to provide a 'default' for things like debugging, or even potentially for things like rendering web pages (for example, the @ construct in Razor writes the ToString() result of a non-string expression to the output stream).

I'm just going to give you the answer straight from the Framework Design Guidelines from the .NET Development Series.

AVOID throwing exceptions from ToString

CONSIDER returning a unique string associated with the instance.

CONSIDER having the output of ToString be a valid input for any parsing methods on this type.

DO ensure that ToString has no observable side effects.

DO report security-sensitive information through an override of ToString only after demanding an appropriate permission. If the permission demand fails, return a string excluding security-sensitive information.

The Object.ToString method is intended to be used for general display and debugging purposes. The default implementation simply provides the object type name. The default implementation is not very useful, and it is recommended that the method be overridden.

DO override ToString whenever an interesting human-readable string can be returned. The default implementation is not very useful, and a custom implementation can almost always provide more value.

DO prefer a friendly name over a unique but not readable ID.

It is also worth mentioning as Chris Sells also explains in the guidelines that ToString is often dangerous for user interfaces. Generally my rule of thumb is to expose a property that would be used for binding information to the UI, and leave the ToString override for displaying diagnostic information to the developer. You can also decorate your type with DebuggerDisplayAttribute as well.

DO try to keep the string returned from ToString short. The debugger uses ToString to get a textual representation of an object to be shown to the developer. If the string is longer than the debugger can display, the debugging experience is hindered.

DO string formatting based on the current thread culture when returning culture-dependent information.

DO provide overload ToString(string format), or implement IFormattable, if the string return from ToString is culture-sensitive or there are various ways to format the string. For example, DateTime provides the overload and implements IFormattable.

DO NOT return an empty string or null from ToString

I swear by these guidelines, and you should to. I can't tell you how my code has improved just by this one guideline for ToString. The same thing goes for things like IEquatable(Of T) and IComparable(Of T). These things make your code very functional, and you won't regret taking the extra time to implement any of it.

Personally, I've never really used ToString much for user interfaces, I have always exposed a property or method of some-sort. The majority of the time you should use ToString for debugging and developer purposes. Use it to display important diagnostic information.

When defining structs (effectively user-primitives) I find it's good practice to have matching ToString, and Parse and TryParse methods, particularly for XML serialization. In this case you will be converting the entire state to a string, so that it can be read from later.

Classes however are more compound structures that will usually be too complex for using ToString and Parse. Their ToString methods, instead of saving the entire state, can be a simple description that helps you identify their state, like a unique identifier like a name or ID, or maybe a quantity for a list.

Also, as Robbie said, overriding ToString allows you to call ToString on a reference as basic as type object.

In some cases it makes it easier to read values of custom classes in the debugger watch window. If I know exactly what I want to see in the watch window, when I override ToString with that information, then I see it.

One benefit of overriding ToString() is Resharper's tooling support: Alt + Ins -> "Formatting members" and it writes the ToString() for you.

You can use that when you have an object with not intuitive meaning of string representation, like person. So if you need for example to print this person you can use this override for preparing it's format.

Simpler depends on how your property is going to be used. If you just need to format the string one time then it does not make that much sense overriding it.

However, It appears that you are overriding the ToString method to not return the normal string data for your property , but to perform a standard formatting pattern. Since you are using string.format with padding.

Because you said you are learning, the exercise appears to also hit on core principles in object oriented programming relating to encapsulation and code re-use.

The string.format taking the arguments you have set for padding ensures that the property will be formatted the same way each time for any code that calls it. As well, going forward you only have to change it in one place instead of many.

Great question and also some great answers!

While I think the most useful information has already been provided, I shall add my two cents:

  • ToString() is meant to be overridden. Its default implementation returns the type name which, while maybe useful at times (particularly when working with a lot of objects), doesn't suffice in the big majority of times.

  • Remember that, for debugging purposes, you can rely on DebuggerDisplayAttribute. You can read more about it here.

  • As a rule, on POCOs you can always override ToString(). POCOs are a structured representation of data, which usually can become a string.

  • Design ToString to be a textual representation of your object. Maybe its main fields and data, maybe a description of how many items are in the collection, etc.

  • Always try to fit that string into a single line and have only essential information. If you have a Person class with Name, Address, Number etc. properties, return only the main data (Name some ID number).

  • Be careful not to override a good implementation of ToString(). Some framework classes already implement ToString(). Overriding that default implementation is a bad thing: people will expect a certain result from ToString() and get another.

Don't be really afraid of using ToString(). The only thing I'd be careful about is returning sensitive information. Other than that, the risk is minimal. Sure, as some have pointed out, other classes will use your ToString whenever reaching for information. But heck, when does returning the type name will be considered better than getting some actual information?

It's always appropriate but carefully consider the intentions behind what you're displaying

A better question would be to ask:

Why would one override ToString()?

ToString() is the window into an object's state. Emphasis on state as a requirement. Strongly OOP languages like Java/C# abuse the OOP model by encapsulating everything in a class. Imagine you are coding in a language that doesn't follow the strong OOP model; consider whether you'd use a class or a function. If you would use it as a function (ie verb, action) and internal state is only maintained temporarily between input/output, ToString() won't add value.

Like others have mentioned, it's important to consider what you output with ToString() because it could be used by the debugger or other systems.

I like to imagine the ToString method as the --help parameter of an object. It should be short, readable, obvious, and easy to display. It should display what the object is not what it does. With all that in mind let's consider...

Use Case - Parsing a TCP packet:

Not an application-level-only network capture but something with more meat like a pcap capture.

You want to overload ToString() for just the TCP layer so you can print data to the console. What would it include? You could go crazy and parse all of the TCP details (ie TCP is complex)...

Which includes the:

  • Source Port
  • Destination Port
  • Sequence Number
  • Acknowledgment number
  • Data offset
  • Flags
  • Window Offset
  • Checksum
  • Urgent Pointer
  • Options (I'm not even going to go there)

But would you want to receive all that junk if you were calling TCP.ToString() on 100 packets? Of course not, it would be information overload. The easy and obvious choice is also the most sensible...

Expose what people would expect to see:

  • Source Port
  • Destination Port

I prefer a sensible output that's easy for humans to parse but YMMV.

TCP:[destination:000, source:000]

Nothing complex, the output isn't for machines to parse (ie unless people are abusing your code), the intended purpose is for human readability.

But what about all the rest of that juicy info I talked about before, isn't that useful too? I'll get to that but first...


ToString() one of the most valuable and underused methods of all time

For two reasons:

  1. People don't understand what ToString() is for
  2. The base 'Object' class is missing another, equally important, string method.

Reason 1 - Don't abuse the usefulness of ToString():

A lot of people use ToString() to pull a simple string representation of an object. The C# manual even states:

ToString is the major formatting method in the .NET Framework. It converts an object to its string representation so that it is suitable for display.

Display, not further processing. That doesn't mean, take my nice string representation of the TCP packet above and pull the source port using a regex ::cringe::.

The right way to do things is, call ToString() directly on the SourcePort property (which BTW is a ushort so ToString() should already be available).

If you need something more robust to package the state of a complex object for machine parsing you'll be better off using a structured serialization strategy.

Fortunately, such strategies are very common:

  • ISerializable (C#)
  • Pickle (Python)
  • JSON (Javascript or any language that implements it)
  • SOAP
  • etc...

Note: Unless you're using PHP because, herp-derp, there's a function for that ::snicker::

Reason 2 - ToString() is not enough:

I have yet to see a language that implements this at the core but I have seen and used variations of this approach in the wild.

Some of which include:

  • ToVerboseString()
  • ToString(verbose=true)

Basically, that hairy mess of a TCP Packet's state should be described for human readability. To avoid 'beating a dead horse' talking about TCP I'll 'point a finger' at the #1 case where I think ToString() and ToVerboseString() are underutilized...

Use Case - Arrays:

If you primarily use one language, you're probably comfortable with that language's approach. For people like me who jump between different languages, the number of varied approaches can be irritating.

Ie, the number of times this has irritated me is greater than the sum of all of the fingers of every Hindu god combined.

There are various cases where languages use common hacks and a few that get it right. Some require wheel re-inventing, some do a shallow dump, others do a deep dump, none of them work the way I'd like them to...

What I'm asking for is a very simple approach:

print(array.ToString());

Outputs: 'Array[x]' or 'Array[x][y]'

Where x is the number of items in the first dimension and y is the number of items in the second dimension or some value that indicates that the 2nd dimension is jagged (min/max range maybe?).

And:

print(array.ToVerboseString());

Outputs the whole she-bang in pretty-print because I appreciate pretty things.

Hopefully, this sheds some light on a topic that has irked me for a long time. At the very least I sprinkled a little troll-bait for the PHPers to downvote this answer.

I find it useful to override the ToString method on entity classes as it helps quickly identify issues in testing especially when an assertion fails the test console will invoke the ToString method on the object.

But in agreement with what has been said before it's to give a human readable representation of the object in question.

Something no one else has mentioned yet: By overriding ToString(), you can also consider implementing IFormattable so you can do the following:

 public override ToString() {
return string.Format("{0,-20} {1, -20}", m_work, m_personal);
}


public ToString(string formatter) {
string formattedEmail = this.ToString();
switch (formatter.ToLower()) {
case "w":
formattedEmail = m_Work;
break;
case "p":
formattedEmail = m_Personal;
break;
case "hw":
formattedEmail = string.Format("mailto:{0}", m_Work);
break;
}
return formattedEmail;
}

Which can be useful.

The Object.ToString method should be used for debugging purposes only. The default implementation shows the object type name which is not very useful. Consider to override this method to provide better information for diagnostics and debugging. Please consider that logging infrastructures often use the ToString method as well and so you will find these text fragments in your log files.

Do not return localized text resources within the Object.ToString method. The reason is that the ToString method should always return something that the developer can understand. The developer might not speak all languages which the application supports.

Implement the IFormattable interface when you want to return a user-friendly localized text. This interface defines a ToString overload with the parameters format and formatProvider. The formatProvider helps you to format the text in a culture aware way.

See also: Object.ToString and IFormattable

I'm happy with the Framework Guidelines referred to in other answers. However, I'd like to emphasize the display and debugging purposes.

Be careful about how you use ToString in your code. Your code shouldn't rely on the string representation of an object. If it does, you should absolutely provide the respective Parse methods.

Since ToString can be used everywhere, it can be a maintainability pain point if you want to change the string representation of an object at a later point in time. You cannot just examine the call hierarchy in this case to study whether some code will break.