Effective C#之5:Always Provide ToString()

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item5  Always Provide ToString()

始终提供ToString()

System.Object.ToString() is one of the most used methods in the .NET environment. You should write a reasonable version for all the clients of your class. Otherwise, you force every user of your class to use the properties in your class and create a reasonable human-readable representation. This string representation of your type can be used to easily display information about an object to users: in Windows Forms, web forms, or console output. The string representation can also be useful for debugging. Every type that you create should provide a reasonable override of this method. When you create more complicated types, you should implement the more sophisticated IFormattable.ToString(). Face it: If you don't override this routine, or if you write a poor one, your clients are forced to fix it for you.

System.Object.ToString().NET环境下使用最多的方法之一,应该为自己的类的所有客户都写一个合理的版本,否则,该类的所有用户就被迫使用这个类的属性,并创建一个合理的易读的表示法。该类型的字符串表示可以简单的为用户(Window Form, web Form或者终端输出)表现一个对象的信息。字符串表示对于调试也很有用。你创建的每个类型都应该提供一个合理的对该方法的重写。当你创建更复杂的类型时,应该实现更精准的IFormattable.ToString()。面对它吧:如果你没重写这个方法,或者你写了一个比较差劲的方法,你的客户就被迫得为你修复它。

The System.Object version returns the name of the type. It's useless information: "Rect", "Point", "Size" is not what you want to display to your users. But that's what you get when you don't override ToString() in your classes. You write a class once, but your clients use it many times. A little more work when you write the class pays off every time you or someone else uses it.

System.Object版本的ToString()返回类型的名称,这是无用的信息:“Rect”、“Point”“Size”这些并不是你想显示给用户的东西,但是如果你不重写ToString()的话,这就是你所得到的。你一次写一个类,但是你的客户要多次使用它。每次当你写这个类的时候多做一点工作,你或他人使用的时候就得到了回报。

Let's consider the simplest requirement: overriding System.Object.ToString(). Every type you create should override ToString() to provide the most common textual representation of the type. Consider a Customer class with three fields:

让我们考虑最简单的需求:重写System.Object.ToString()。你创建的每个类型都应该重写ToString()方法,为该类型提供最通用的文字表述层。考虑一个具有3个字段的Customer类:

  1.     public class Customer
  2.     {
  3.         private String name;
  4.         private Decimal revenue;
  5.         private String contactPhone;
  6.  }

The inherited version of Object.ToString() returns "Customer". That is never useful to anyone. Even if ToString() will be used only for debugging purposes, it should be more sophisticated than that. Your override of Object.ToString() should return the textual representation most likely to be used by clients of that class. In the Customer example, that's the name:

继承自Object.ToString()的版本返回“Customer”,那对任何人都是没用的。甚至就算ToString()仅被用作调试使用时,也应比这个更精准。你的重写的Object.ToString()应返回最可能被该类的客户使用的文字表述,在Customer类里面就是name

  1.         public override string ToString()
  2.         {
  3.             return name;
  4.      }

If you don't follow any of the other recommendations in this item, follow that exercise for all the types you define. It will save everyone time immediately. When you provide a reasonable implementation for the Object.ToString() method, objects of this class can be more easily added to Windows Forms controls, web form controls, or printed output. The .NET FCL uses the override of Object.ToString() to display objects in any of the controls: combo boxes, list boxes, text boxes, and other controls. If you create a list of customer objects in a Windows Form or a web form, you get the name displayed as the text. System.Console.WriteLine() and System.String.Format(), as well as ToString() internally. Anytime the .NET FCL wants to get the string representation of a customer, your customer type supplies that customer's name. One simple three-line method handles all those basic requirements.

如果你不遵循本条款里面的任何其它建议,那么就为你定义的所有类型仿照这个练习提供一个ToString()方法吧,这会迅速节省每个人的时间。当你提供了对Object.ToString()方法的合理实现时,该类的对象就能够更容易的被加入到Windows Form控件、web Form控件或者打印输出中。.NET FCL使用重写的Object.ToString()来显示下列任何控件中的对象:combo boxeslist boxestext boxes和其他控件。如果你在Windows Formweb Form中创建了一个用户对象的列表,你会得到以文字显示的名字。System.Console.WriteLine()System.String.Format()ToString()的内在是一致的。任何时间,.Net FCL想得到一个客户的字符串表述时,你编写的客户类就提供客户的名字。一个简单的三行的方法就处理了所有这些基本要求。

This one simple method, ToString(), satisfies many of the requirements for displaying user-defined types as text. But sometimes, you need more. The previous customer type has three fields: the name, the revenue, and a contact phone. The System.ToString() override uses only the name. You can address that deficiency by implementing the IFormattable interface on your type. IFormattable contains an overloaded ToString() method that lets you specify formatting information for your type. It's the interface you use when you need to create different forms of string output. The customer class is one of those instances. Users will want to create a report that contains the customer name and last year's revenue in a tabular format. The IFormattable.ToString() method provides the means for you to let users format string output from your type. The IFormattable.ToString() method signature contains a format string and a format provider:

这个简单的方法,ToString(),满足了多数要求:以文字的方式显示用户定义的类型,但是有时你需要更多。前面的customer类型有三个字段:namerevenuecontact phone。对System.ToString()的重写只用到了名字。你可以通过实现IFormattable接口来处理这个不足。IFormattable含有一个重载的ToString()方法,可以为你的类型指定格式化信息。这是当你需要创建不同形式的字符串输出时使用的接口。Customer类是这些实例中的一个,使用者会希望以列表的格式生成一个含有客户名字和上年税收的报告。IFormattable.ToString()方法给你提供了方式:让用户可以从你的类中格式化字符串输出。IFormattable.ToString()方法签名包含一个格式化字符串和一个格式化提供者:

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

You can use the format string to specify your own formats for the types you create. You can specify your own key characters for the format strings. In the customer example, you could specify n to mean the name, r for the revenue, and p for the phone. By allowing the user to specify combinations as well, you would create this version of IFormattable.ToString():

你可以使用格式化字符串为自己创建的类型指定自己的格式,可以为格式化字符串指定自己的关键字符。在customer的例子里,你可以指定n来表示名字,r表示税收,p表示电话。通过允许用户指定格式的组合,你可以创建IFormattable.ToString()的这个版本:

  1. //supported formats:
  2. //substitute n for name.
  3. //substitute r for revenue
  4. //substitute p for contact phone
  5. //Combos are supported:nr,np,npr,etc
  6. //"G" is general
  7. String  System.IFormattable.ToString(String format, IFormatProvider formatProvider)
  8. {
  9.     if (formatProvider != null)
  10.     {
  11.         ICustomFormatter fmt = formatProvider.GetFormat(this.GetType()) as ICustomFormatter;
  12.         if (fmt != null)
  13.             return fmt.Format(format, this, formatProvider);
  14.     }
  15.     switch (format)
  16.     {
  17.         case "r":
  18.             return revenue.ToString();
  19.         case "p":
  20.             return contactPhone;
  21.         case "nr":
  22.             return String.Format("{0,20},{1,10:C}", name, revenue);
  23.         case "np":
  24.             return String.Format("{0,20},{1,15}", name, contactPhone);
  25.         case "pr":
  26.             return String.Format("{0,15},{1,10:C}", contactPhone, revenue);
  27.         case "pn":
  28.             return String.Format("{0,15},{0,20}", contactPhone, name);
  29.         case "rn":
  30.             return String.Format("{1,10:C},{0,20}",revenue,name);
  31.         case "rp":
  32.             return String.Format("{1,10:C},{0,15}",revenue,contactPhone);
  33.         case "nrp":
  34.             return String.Format("{0,20},{1,10:C},{0,15}", name, revenue, contactPhone);
  35.         case "npr":
  36.             return String.Format("{0,20},{0,15},{1,10:C}}", name, contactPhone,revenue);
  37.         case "pnr":
  38.             return String.Format("{0,15},{0,20},{1,10:C}}", contactPhone,name ,revenue);
  39.         case "prn":
  40.             return String.Format("{0,15},{1,10:C},{0,20}}", contactPhone, revenue,name);
  41.         case "rpn":
  42.             return String.Format("{1,10:C},{0,15},{0,20}}", revenue,contactPhone, name);
  43.         case "rnp":
  44.             return String.Format("{1,10:C},{0,20},{0,15}}", revenue,name,contactPhone);
  45.         case "n":
  46.         case "G":
  47.         default:
  48.             return name;
  49.     }
  50. }

Adding this function gives your clients the capability to specify the presentation of their customer data:

加入了这个功能,让你的客户具有这个能力:指定他们的顾客数据的表述:

  1. IFormattable c1 = new Customer();
  2. Console.WriteLine("Customer record: {0}", c1.ToString("nrp"null));

Any implementation of IFormattable.ToString() isspecific to the type, but you must handle certain cases whenever you implement the IFormattable interface. First, you must support the general format, "G". Second, you must support the empty format in both variations: "" and null. All three format specifiers must return the same string as your override of the Object.ToString() method. The .NET FCL calls IFormattable.ToString() instead of Object.ToString() for every type that implements IFormattable. The .NET FCL usually calls IFormattable.ToString() with a null format string , but a few locations use the "G" format string, to indicate the general format. If you add support for the IFormattable interface and do not support these standard formats, you've broken the automatic string conversions in the FCL.

IFormattable.ToString()的任何实现都和这个类型的细节有关,无论何时你实现IFormattable接口的时候,都应该处理一些情况。首先,你应该支持通用的格式“G”,第二,应该支持变量的每种空格式:“”和null。但你重写Object.ToString()时,这三种不同的格式化指定应该返回同样的字符串。.NET FCL为实现IFormattable的每种类型调用IFormattable.ToString()来替代Object.ToString().NET FCL通常使用null格式化字符串来调用IFormattable.ToString(),但是有一部分地区使用“G”格。如果你加入了对IFormattable接口的支持却不支持这些标准的格式,就破坏了FCL中的自动字符串转换。

The second parameter to IFormattable.ToString() is an object that implements the IFormatProvider interface. This object lets clients provide formatting options that you did not anticipate. If you look at the previous implementation of IFormattable.ToString(), you will undoubtedly come up with any number of format options that you would like but that you find lacking. That's the nature of providing human-readable output. No matter how many different formats you support, your users will one day want some format that you did not anticipate. That's why the first few lines of the method look for an object that implements IFormatProvider and delegate the job to its ICustomFormatter.

IFormattable.ToString()的第二个参数,是实现IFormatProvider接口的一个对象。这个对象允许客户提供你原没有预测到的格式化选项。如果你看看前面的对IFormattable.ToString()的实现,就不会怀疑可能会碰到任何数量的你缺失的格式化选项了。这就是提供易读的输出的本质。无论你提供多少不同的格式化输出,有一天,你的用户都会想要一些你没有预测到的格式。这就是为什么在方法的前面几行,寻找实现了IFormatProvider的对象,并将工作委托给ICustomFormatter.

Shift your focus now from class author to class consumer. You find that you want a format that is not supported. For example, you have customers whose names are longer than 20 characters, and you want to modify the format to provide a 50-character width for the customer name. That's why the IFormatProvider interface is there. You create a class that implements IFormatProvider and a companion class that implements ICustomFormatter to create your custom output formats. The IFormatProvider interface defines one method: GetFormat().GetFormat() returns an object that implements the ICustomFormatter interface. The ICustomFormatter interface specifies the method that does the actual formatting. The following pair creates modified output that uses 50 columns for the customer name:

现在,将你的注意力从类的作者转移到类的使用者。你发现你需要一个不被支持的格式。例如,你有一个客户的名字长度大于了20个字符,因此你想修改这个格式使其对50个字符长度的客户名字提供支持,这就是为什么使用了IFormatProvider接口。你创建一个实现了IFormatProvider的类,一个实现了ICustomFormatter的辅助类来实现指定的输出格式。IFormatProvider接口定义了一个方法:GetFormat()GetFormat()返回一个实现ICustomFormat接口的对象。ICustomFormatter接口指定了做实际格式化工作的方法。下面创建了修改后的输出:使客户名字使用50个列的宽度。

 

  1.    //Example IFormatProvider
  2.     public class CustomFormatter : IFormatProvider
  3.     {
  4.         #region IFormatProvider Members
  5.         //IFormatProvider contains one method.
  6.         //This method returns an object that
  7.         //formats using the requested interface.
  8.         //Typically ,only the ICustomFormatter
  9.         //is iplemented
  10.         public object GetFormat(Type formatType)
  11.         {
  12.             if (formatType == typeof(ICustomFormatter))
  13.                 return new CustomerFormatProvider();
  14.             return null;
  15.         }
  16.         #endregion
  17.  
  18.         //Nested class to provide the
  19.         //custom formatting for the Customer class
  20.         private class CustomerFormatProvider : ICustomFormatter
  21.         {
  22.             #region ICustomFormatProvider Members
  23.             public String Format(String format, Object arg, IFormatProvider formatProvider)
  24.             {
  25.                 Customer c = arg as Customer;
  26.                 if (c == null)
  27.                     return arg.ToString();
  28.                 return String.Format("{0,50},{1,15},{2,10:C}", c.Name, c.ContactPhone, c.Revenue);
  29.             }
  30.             #endregion
  31.         }
  32. }

The GetFormat() method creates the object that implements the ICustomFormatter interface. The ICustomFormatter.Format() method does the actual work of formatting the output in the requested manner. That one method translates the object into a string format. You can define the format strings for ICustomFormatter.Format() so that you can specify multiple formats in one routine. The FormatProvider will be the IFormatProvider object from the GetFormat() method.

GetFormat()方法创建了一个实现了ICustomFormatter接口的对象。ICustomFormatter.Format()方法以被要求的方式做了实际的格式化工作。这个方法将这个对象转换成了字符串格式。可以为ICustomFormatter.Format()定义格式化字符串,那样的话就可以在一个子程序里面指定多种格式。FormatProvider将会成为来自GetFormat()方法的IFormatProvider对象。

To specify your custom format, you need to explicitly call string.Format() with the IFormatProvider object:

为了指定你自己的格式,需要显式的调用带有IFormatProvider对象参数的String.Format()方法:

  1. Console.WriteLine(String.Format(new CustomFormatter(),"",c1));

You can create IFormatProvider and ICustomFormatter implementations for classes whether or not the class implemented the IFormattable interface. So, even if the class author didn't provide reasonable ToString() behavior, you can make your own. Of course, from outside the class, you have access to only the public properties and data members to construct your strings. Writing two classes, IFormatProvider and IcustomFormatter, is a lot of work just to get text output. But implementing your specific text output using this form means that it is supported everywhere in the .NET Framework.

无论一个类是否实现了IFormattable接口,你都可以为它创建IFormatProviderICustomFormatter的实现。因此,即使类的作者没有提供合理的ToString()行为,你仍然可以自己实现。当然,从类的外部来说,你仅仅能够访问公共属性和数据成员来创建自己的字符串。编写IFormatProvider ICustomFormatter2个类,对于得到的仅仅一个文字输出来说,是很多的工作。但是使用这种方式来实现你自己的文字输出意味着:在.Net框架下的任何地方都是被支持的。

So now step back into the role of class author again. Overriding Object.ToString() is the simplest way to provide a string representation of your classes. You should provide that every time you create a type. It should be the most obvious, most common representation of your type. On those rarer occasions when your type is expected to provide more sophisticated output, you should take advantage of implementing the IFormattable interface. It provides the standard way for users of your class to customize the text output for your type. If you leave these out, your users are left with implementing custom formatters. Those solutions require more code, and because users are outside of your class, they cannot examine the internal state of the object.

现在让我们再次回到类的作者的角色中。重写Object.ToString()是为你的类提供字符串表述的最简单方式。你应该在每次创建一个类的时候都提供这个方法,它应该是对你的类的最显而易见的、最通用的表述。在其他一些较少的情况下,当需要提供更加精准的输出时,应该利用对IFormattable接口的实现,它为类的使用者提供了标准的方法,来指定类的文字性输出。如果你对这些放手不管,你的用户就得实现自己的格式化对象了。这样的解决方法需要更多的代码,因为用户是在你的类的外部,不能检查对象的内部状态。

Eventually, people consume the information in your types. People understand text output. Provide it in the simplest fashion possible: Override ToString() in all your types.

最后,人们可以使用你的类的这些信息了,大家都理解文字性的输出。用最简单的最流行的方法来提供它:在你的所有类里面重写ToString()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值