[ Team LiB ] |
Recipe 3.2 Allowing a Type to Represent Itself as a StringProblemYour class or structure needs to control how its information is displayed when its ToString method is called. For example, when creating a new data type, such as a Line class, you might want to allow objects of this type to be able to display themselves in a textual format. In the case of a Line object, it would display itself as "(x1, y1)(x2, y2)". SolutionOverride and/or implement the IFormattable.ToString method to display numeric information, such as for a Line structure: using System; using System.Text; using System.Text.RegularExpressions; public struct Line : IFormattable { public Line(int startX, int startY, int endX, int endY) { x1 = startX; x2 = endX; y1 = startY; y2 = endY; } public int x1; public int y1; public int x2; public int y2; public double GetDirectionInRadians( ) { int xSide = x2 - x1; int ySide = y2 - y1; if (xSide == 0) // Prevent divide-by-zero return (0); else return (Math.Atan (ySide / xSide)); } public double GetMagnitude( ) { int xSide = x2 - x1; int ySide = y2 - y1; return (Math.Sqrt( Math.Sqrt((xSide * xSide) + (ySide * ySide)) )); } // This overrides the Object.ToString method // This override is not required for this recipe // and is included for completeness public override string ToString( ) { return (String.Format("({0},{1}) ({2},{3})", x1, y1, x2, y2)); } public string ToString(string format) { return (this.ToString(format, null)); } public string ToString(IFormatProvider formatProvider) { return (this.ToString(null, formatProvider)); } public string ToString(string format, IFormatProvider formatProvider) { StringBuilder compositeStr = new StringBuilder(""); if ((format != null) && (format.ToUpper( ).Equals("V"))) { double direction = this.GetDirectionInRadians( ); double magnitude = this.GetMagnitude( ); string retStringD = direction.ToString("G5", formatProvider); string retStringM = magnitude.ToString("G5", formatProvider); compositeStr.Append("magnitude = ").Append(retStringM).Append ("\tDirection = ").Append(retStringD); } else { string retStringX1 = this.x1.ToString(format, formatProvider); string retStringY1 = this.y1.ToString(format, formatProvider); string retStringX2 = this.x2.ToString(format, formatProvider); string retStringY2 = this.y2.ToString(format, formatProvider); compositeStr.Append("(").Append(retStringX1).Append(",").Append (retStringY1).Append(")(").Append(retStringX2).Append (",").Append(retStringY2).Append(")"); } return (compositeStr.ToString( )); } } DiscussionThe ToString method provides a convenient way to display the current contents, or state, of a structure (this recipe works equally well for reference types). The solution section of this recipe shows the various implementations of ToString for both numeric and textual data. The Line class contains two points in space that form the endpoints of a line. This line data is then fed into the ToString methods for that class to produce formatted output. The following code exercises the ToString methods of the Line class: using System.Globalization; public void TestLineToString( ) { Line V1 = new Line(0, 0, 40, 123); Line V2 = new Line(0, -2, 1, 11); Line V3 = new Line(0, 1, 0, 1); Console.WriteLine("\r\nTest Default ToString method"); Console.WriteLine("V1 = " + V1); Console.WriteLine("V2 = " + V2); Console.WriteLine("V1.ToString( ) = {0:V}", V1.ToString( )); Console.WriteLine("V2.ToString( ) = {0:V}", V2.ToString( )); Console.WriteLine("\r\nTest overloaded ToString(format) method"); Console.WriteLine("V1.ToString(\"D\") = {0:D}", V1); Console.WriteLine("V1.ToString(\"D5\") = {0:D5}", V1); Console.WriteLine("V2.ToString(\"F\") = {0:F}", V2); Console.WriteLine("V1.ToString(\"N\") = {0:N}", V1); Console.WriteLine("V2.ToString(\"n\") = {0:n}", V2); Console.WriteLine("V1.ToString(\"E\") = {0:E}", V1); Console.WriteLine("V2.ToString(\"X\") = {0:X}", V2); Console.WriteLine("\r\nTest overloaded ToString(formatProvider) method"); NumberFormatInfo NullFormatter = null; NumberFormatInfo Formatter = new NumberFormatInfo( ); Formatter.NegativeSign = "!"; Formatter.PositiveSign = "+"; Console.WriteLine("V2.ToString(Formatter) = " + V2.ToString(Formatter)); Console.WriteLine("V2.ToString(Formatter) = " + V2.ToString(Formatter)); Console.WriteLine("V2.ToString(null) = " + V2.ToString(NullFormatter)); Console.WriteLine("V2.ToString(null) = " + V2.ToString(NullFormatter)); Console.WriteLine("V2.ToString(new CultureInfo(\"fr-BE\")) = " + V2.ToString(new CultureInfo("fr-BE"))); //French - Belgium Console.WriteLine("V2.ToString(new CultureInfo(\"fr-BE\")) = " + V2.ToString(new CultureInfo("fr-BE"))); //French - Belgium Console.WriteLine ("\r\nTest overloaded ToString(format, formatProvider) method"); Console.WriteLine("V2.ToString(\"D\", Formatter) = " + V2.ToString("D", Formatter)); Console.WriteLine("V2.ToString(\"F\", Formatter) = " + V2.ToString("F", Formatter)); Console.WriteLine("V2.ToString(\"D\", null) = " + V2.ToString("D", null)); Console.WriteLine("V2.ToString(\"F\", null) = " + V2.ToString("F", null)); Console.WriteLine("V2.ToString(\"D\", new CultureInfo(\"fr-BE\")) = " + V2.ToString("D", new CultureInfo("fr-BE"))); //French - Belgium Console.WriteLine("V2.ToString(\"F\", new CultureInfo(\"fr-BE\")) = " + V2.ToString("F", new CultureInfo("fr-BE"))); //French - Belgium Console.WriteLine("\r\nTest overloaded ToString(\"V\", formatProvider) method"); Console.WriteLine("V2.ToString(\"V\", Formatter) = " + V2.ToString("V", Formatter)); Console.WriteLine("V2.ToString(\"V\", null) = " + V2.ToString("V", null)); } This code displays the following results: Test Default ToString method V1 = (0,0) (40,123) V2 = (0,-2) (1,11) V1.ToString( ) = (0,0) (40,123) V2.ToString( ) = (0,-2) (1,11) Test overloaded ToString(format) method V1.ToString("D") = (0,0)(40,123) V1.ToString("D5") = (00000,00000)(00040,00123) V2.ToString("F") = (0.00,-2.00)(1.00,11.00) V1.ToString("N") = (0.00,0.00)(40.00,123.00) V2.ToString("n") = (0.00,-2.00)(1.00,11.00) V1.ToString("E") = (0.000000E+000,0.000000E+000)(4.000000E+001,1.230000E+002) V2.ToString("X") = (0,FFFFFFFE)(1,B) Test overloaded ToString(formatProvider) method V2.ToString(Formatter) = (0,!2)(1,11) V2.ToString(Formatter) = (0,!2)(1,11) V2.ToString(null) = (0,-2)(1,11) V2.ToString(null) = (0,-2)(1,11) V2.ToString(new CultureInfo("fr-BE")) = (0,-2)(1,11) V2.ToString(new CultureInfo("fr-BE")) = (0,-2)(1,11) Test overloaded ToString(format, formatProvider) method V2.ToString("D", Formatter) = (0,!2)(1,11) V2.ToString("F", Formatter) = (0.00,!2.00)(1.00,11.00) V2.ToString("D", null) = (0,-2)(1,11) V2.ToString("F", null) = (0.00,-2.00)(1.00,11.00) V2.ToString("D", new CultureInfo("fr-BE")) = (0,-2)(1,11) V2.ToString("F", new CultureInfo("fr-BE")) = (0,00,-2,00)(1,00,11,00) Test overloaded ToString("V", formatProvider) method V2.ToString("V", Formatter) = magnitude = 3.6109 direction = 1.494 V2.ToString("V", null) = magnitude = 3.6109 direction = 1.494 This method prints out the two x and y coordinates that make up the start and end points of a line for the Line class. An example output of the Line.ToString( ) method is: (0,0) (40,123) This output could also be displayed as a vector that starts at the origin of the Cartesian plane and points straight up along the positive y-axis. Another choice for this would be to print out the magnitude and direction of this line. This result is demonstrated in the overloaded ToString method that accepts both a format string and an IFormatProvider. The next overloaded ToString method takes a single argument, format, which is a String containing the formatting information of the type. This method calls the last overloaded ToString method and passes the format information as the first parameter and a null as the second parameter. The following ToString method operates similarly to the previous ToString method, except that it accepts an IFormatProvider data type as its only parameter. The format parameter of the last ToString method is set to null when called by this method. The final ToString method is where all the real work takes place. This method accepts two parameters, a String (format) containing formatting information and an IFormatProvider (formatProvider) containing even more specific formatting information. The format string makes use of predefined formats such as "D", "d", "F", "f", "G", "g", "X", and "x", to name a few. (See Recipe 2.16 for more information on the formatting character codes.) These formats specify whether the information will be displayed as decimal ("D" or "d"), general ("G" or "g"), hexadecimal ("X" or "x"), or one of the other types. As a note, calling ToString with no parameters always sets the format type to general. In addition, this method also takes a special format character "V" or "v". This character formatting code is not one of the predefined formatting codes; instead, it is one that we added to provide special handling of a Line object's output in vector format. This code allows the Line type to be displayed as a magnitude and a direction: magnitude = 13.038 direction = 1.494 The second parameter accepts any data type that implements the IFormatProvider interface. There are three types in the FCL that implement this interface: CultureInfo, DateTimeFormatInfo, or NumberFormatInfo. The CultureInfo class contains formatting information specific to the various supported cultures that exist around the world. The DateTimeFormatInfo class contains formatting information specific to date and time values; similarly, the NumberFormatInfo class contains formatting information specific to numbers. This ToString method sets up a variable, compositeStr, which will contain the final formatted value of the Line type. Next, the format parameter is checked for null. Remember, the previous ToString method that accepts the IFormatProvider parameter will call this form of the ToString method and pass in a format value of null. So we must be able to handle a null value gracefully at this point. If the format parameter passed in to the Line type is not null and is equal to the character "V", we are able to provide a string to display this line as a magnitude and a direction. The direction and magnitude values are obtained for this object and are displayed in a General format with five significant digits of precision. If, on the other hand, any other type of formatting character code was passed in—including null—each of the individual coordinates are formatted using the ToString method of the Int32 structure. These coordinates are concatenated into a string and returned to the caller to be displayed. The method: public string ToString(string format, IFormatProvider formatProvider) must be implemented, since the structure implements the IFormattable interface. The IFormattable interface provides a consistent interface for this ToString method: public interface IFormattable { string ToString(string format, IFormatProvider formatProvider); } For the Line structure, the IFormattable.ToString method passes its parameters to the Int32 structure's ToString method with the same method signature, which provides a more uniform formatting capability for Line values.
See AlsoSee Recipe 2.16; see the "IFormatProvider Interface" topic in the MSDN documentation. |
[ Team LiB ] |