Objective C # Principle 5: always provide tostring ()

Source: Internet
Author: User

In the. NET world, one of the most used methods is system. Object. tostrying. You should write a "rational" class for all your customers ). Either, you force the users of the class to use the class attributes and add some reasonable easy-to-read instructions. This is a string. The description of the class you designed can easily show your users some information about the object to: Windows form, web form, console output. These character descriptions can be used for debugging. This method should be properly rewritten for any type you write. When you design more complex types, you should implement more iformattable. tostring (). admit this: if you do not override this conventional method, or just write a bad one, your customers will have to fix it for you.

The tostring () method of system. object only returns the type name. There is not much useful information: "rect", "point", and "size" won't be displayed to your users as you think. But that is only obtained when you did not override the tostring () method for your class. You only write for your class once, but your customers will use it many times. When you design a class, you can add a little more work to get a return when you or others use it.
(Nonsense !)

Let's consider a simple requirement: rewrite the system. Object. tostring () method. The tostring () method should be rewritten for each type you designed to provide some of the most common text descriptions for your type. Consider this customer class and its three members (fields, where it is related to the database, it refers to the Field ):

public class Customer{    private string _name;    private decimal _revenue;    private string _contactPhone;}

By default, the tostring () method inherited from system. Object returns "customer ". This will not be of great help to everyone. Even if tostring () is only used for debugging, it should be more flexible (sophisticated. The tostring () method you override should return text instructions, more like your users are using this class. In the customer example, this should be the name:

public override string ToString(){    return _name;}


If you do not comply with other comments in this principle, rewrite the method for all the types defined in the above method. It saves everyone time.

When you responsibly rewrite the object. tostring () method, the objects of this class can be easily added to Windows form, web form, or printed out. The. net fcl uses the overloaded object. tostring () to display objects in the control: combo box, list box, text box, and some other controls. If you add a linked list of customer objects to a Windows form or web form, you will get their names (in text) displayed: instead of the same type name for each object ).

Syste. Console. writeline () and system. String. formate () are the same internally (implemented. At any time, when the. net fcl wants to obtain a string description of a customer, your customer type will provide a customer name. A simple function with only three rows completes all the basic requirements.

This is a simple method. tostring () can also meet the needs of many custom types by text (output method. But sometimes you may have more requirements. The preceding customer type has three members: name, revenue, and contact number. Only _ name is used for rewriting system. Object. tostring. You can make up for this deficiency by implementing the iformattable interface. This is an interface used when you need to output formatted text externally. Iformattable contains an overloaded tostring () method. You can use this method to specify a detailed format for your type information. This is also an interface used when you want to generate and output strings of multiple formats. This is the case for the customer class. The user will want to generate a report that contains the table-based usernames and last year's income. The iformattable. tostring () method is exactly what you want. It allows users to format and output your type information. The prototype parameters of this method contain a formatting string and a formatting engine:

string System.IFormattable.ToString( string format,     IFormatProvider formatProvider )

You can specify the format string to be used for the type you designed. You can also specify key characters for your format string. In this customer example, you can use N to represent the name, R to represent the income, and P to represent the phone. In this way, your users can combine specified information at will, and you need to provide the following iformattable. tostring () for your type ():

#region IFormattable Members// supported formats:// substitute n for name.// substitute r for revenue// substitute p for contact phone.// Combos are supported:  nr, np, npr, etc// "G" is general.string System.IFormattable.ToString( string format,  IFormatProvider formatProvider ){  if ( formatProvider != null )  {    ICustomFormatter fmt = formatProvider.GetFormat(      this.GetType( ) )      as ICustomFormatter;    if ( fmt != null )      return fmt.Format( format, this, formatProvider );  }  switch ( format )  {    case "r":      return _revenue.ToString( );    case "p":      return _contactPhone;    case "nr":      return string.Format( "{0,20}, {1,10:C}",        _name, _revenue );    case "np":      return string.Format( "{0,20}, {1,15}",        _name, _contactPhone );    case "pr":      return string.Format( "{0,15}, {1,10:C}",        _contactPhone, _revenue );    case "pn":      return string.Format( "{0,15}, {1,20}",        _contactPhone, _name );    case "rn":      return string.Format( "{0,10:C}, {1,20}",        _revenue, _name );    case "rp":      return string.Format( "{0,10:C}, {1,20}",        _revenue, _contactPhone );    case "nrp":      return string.Format( "{0,20}, {1,10:C}, {2,15}",        _name, _revenue, _contactPhone );    case "npr":      return string.Format( "{0,20}, {1,15}, {2,10:C}",        _name, _contactPhone, _revenue );    case "pnr":      return string.Format( "{0,15}, {1,20}, {2,10:C}",        _contactPhone, _name, _revenue );    case "prn":      return string.Format( "{0,15}, {1,10:C}, {2,15}",        _contactPhone, _revenue, _name );    case "rpn":      return string.Format( "{0,10:C}, {1,15}, {2,20}",        _revenue, _contactPhone, _name );    case "rnp":      return string.Format( "{0,10:C}, {1,20}, {2,15}",        _revenue, _name, _contactPhone );    case "n":    case "G":    default:      return _name;  }}#endregion

The above practice is obviously unreasonable. If my object has 10 members, such a combination will make people crazy. We recommend that you use regular expressions to do this. Regular Expressions are excellent in text processing .)

After adding such a function, you have the ability to specify the customer data as follows:

Iformattable C1 = new customer ();

Console. writeline ("customer record: {0 }",

C1.tostring ("NRP", null ));

Any implementation of iformattable. tostring () must specify the type. However, no matter when you implement the iformattation interface, you must handle the case. First, you must support the format character "G ". Second, you must support two empty formatting characters: "" and null. When you overload the object. tostring () method, the three formatting characters should return the same string .. Net FCL often uses null to call iformattable. tostring () method to replace object. tostring () is called, but in a few places the format character "G" is used to format the string, thus distinguishing the common format. If you have added support for the iformattable interface and do not support standard formatting, you will destroy the automatic (implicit) Conversion of strings in FCL.

The second parameter of iformattable. tostring () is an object that implements the iformatprovider interface. This object provides users with some formatting options that you have not set in advance (Note: You can only implement your own formatting options, and others are done by default ). If you look at the previous implementation of iformattable. tostring (), you will not hesitate to come up with countless formatting options that you like. These are all not included in formatting. It is natural to support output that is easy to read. However, no matter how many formats you support, your users will one day want the format you did not expect. This is why the first few lines of this method need to check and implement the iformatprovider object, and delegate the work of icustomformatter to it.

Let's move the (discussed) focus from the class author to the class user. You find that the format you want is not supported. For example, you have a group of customers whose names must be greater than 20 characters and you want to modify the formatting options to support the customer names with a length of 50 characters. This is why the iformatprovider interface must exist. You can design a class that implements iformatprovider and enable it to simultaneously implement the icustomformatter interface for formatting the output. The iformatprovider interface defines a method: getformat (). This method returns an object that implements the icustomformatter interface. The method specified by the icustomformatter interface is used to complete the actual formatting. The following interface modifies the output so that it supports a user name with a length of 50 characters:

// Example IFormatProvider:public class CustomFormatter : IFormatProvider{    #region IFormatProvider Members    // IFormatProvider contains one method.    // This method returns an object that    // formats using the requested interface.    // Typically, only the ICustomFormatter    // is implemented    public object GetFormat(Type formatType)    {        if (formatType == typeof(ICustomFormatter))            return new CustomerFormatProvider();        return null;    }    #endregion    // Nested class to provide the    // custom formatting for the Customer class.    private class CustomerFormatProvider : ICustomFormatter    {        #region ICustomFormatter Members        public string Format(string format, object arg,          IFormatProvider formatProvider)        {            Customer c = arg as Customer;            if (c == null)                return arg.ToString();            return string.Format("{0,50}, {1,15}, {2,10:C}",              c.Name, c.ContactPhone, c.Revenue);        }        #endregion    }}

The getformat () method gets an object that implements the icustomformatter interface. The icustomformatter. Format () method is responsible for the actual formatting and output according to user requirements. This method converts an object to a formatted string. You can define a format string for icustomformatter. Format (), so you can specify multiple formats as usual. Formatprovider is an iformatprovider object obtained by the getformat () method.

To meet your formatting requirements, you must use the iformatprovider object to explicitly call the string. Format () method:

Console. writeline (string. Format (New customformatter (), "", C1 ));

You can design a class to implement the iformatprovider and icustomformatter interfaces, and then implement or not implement the iformattable interface. Therefore, even if the author of this class does not provide reasonable tostring behavior, you can do it yourself. Of course, you can only access public attributes to obtain strings from outside the class. Implement two interfaces: iformatprovider and icustomformatter. Only some text output is required. However, in the. NET Framework, all the specified text output you implement can be well supported.

So, return to the author of the class. It is very easy to rewrite object. tostring () to provide instructions for your class. You should provide such support for your type every time. And this should be the most obvious and commonly used description of your type. In some extreme cases, when Formatting cannot support some output that is too flexible, you should use the advantages of the iformattable interface. It provides a standard method for custom formatting output of your type. If you give up, your users will lose the tools used to implement custom formatting. These solutions require more code, and because your users are outside the class, they cannot check the status in the class.

Finally, you notice your type of information and they will understand the output text. Provide such information in a simple way as possible: rewrite the tostring () method for all your types.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.