Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Overriding System.Object.ToString() and Implementing IFormattable

A couple of days ago I discussed overriding System.Object.Equals(Object obj) in a post called Object Identity vs. Object Equality – Overriding System.Object.Equals(Object obj).  The gist of that post is essentially that when you create new classes that inherit from System.Object, you may want to consider overriding System.Object’s virtual methods so that they make sense for your class.  In that article we had overriden Equals and GetHashCode for the Customer class to suit our needs for equality.


In continuing this idea, System.Object also has a virtual ToString() method that we inherit in our classes.  I find this an absolute must to override, because the method provided by System.Object only returns the name of the class.  Hence the output of the following code will just be “Person.”


Not Overriding ToString()

using System;

public class Person
{
#region Private Members

private string _firstname;
private string _lastname;
private int _age;

#endregion

#region Properties

public string Firstname
{
get { return _firstname; }
set { _firstname = value; }
}

public string Lastname
{
get { return _lastname; }
set { _lastname = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}

#endregion

#region Contructors

public Person (string firstname, string lastname, int age)
{
if (firstname == null || firstname.Length == 0)
throw new ArgumentNullException(firstname);

if (lastname == null || lastname.Length == 0)
throw new ArgumentNullException(lastname);

_firstname = firstname;
_lastname
= lastname;
_age
= age;
}

#endregion
}

public class TestClass
{
public static void Main()
{
Person p
= new Person(David,Hayden,99);
Console.WriteLine(p); // Outputs “Person”
Console.ReadLine();
}
}


That is pretty useless for debugging or anythine else.  By simply overriding ToString(), we can get something a bit more useful.  When we run the new version of our code shown below, we get the following output: “David Hayden, Age = 99″.


Overriding ToString()
using System;

public class Person
{
#region Private Members

private string _firstname;
private string _lastname;
private int _age;

#endregion

#region Properties

public string Firstname
{
get { return _firstname; }
set { _firstname = value; }
}

public string Lastname
{
get { return _lastname; }
set { _lastname = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}

#endregion

#region Contructors

public Person (string firstname, string lastname, int age)
{
if (firstname == null || firstname.Length == 0)
throw new ArgumentNullException(firstname);

if (lastname == null || lastname.Length == 0)
throw new ArgumentNullException(lastname);

_firstname = firstname;
_lastname
= lastname;
_age
= age;
}

#endregion

public override string ToString()
{
return String.Format({0} {1}, Age = {2}, _firstname,

                               _lastname, _age.ToString());
}
}

public class TestClass
{
public static void Main()
{
Person p
= new Person(David,Hayden,99);
Console.WriteLine(p); // Outputs “David Hayden, Age = 99″
Console.ReadLine();
}
}


So now we are cooking.  However, in the real world, we need way more flexibility in how we want to display this person.  This is where the interface IFormattable comes in.  IFormattable is as basic as you can get.  It has one method:


IFormattable

public string ToString(string format, IFormatProvider formatProvider);


By having our person class implement IFormattable, we can display this person class in a multitude of formats:


Implementing IFormattable
using System;

public class Person : IFormattable
{
#region Private Members

private string _firstname;
private string _lastname;
private int _age;

#endregion

#region Properties

public string Firstname
{
get { return _firstname; }
set { _firstname = value; }
}

public string Lastname
{
get { return _lastname; }
set { _lastname = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}

#endregion

#region Contructors

public Person (string firstname, string lastname, int age)
{
if (firstname == null || firstname.Length == 0)
throw new ArgumentNullException(firstname);

if (lastname == null || lastname.Length == 0)
throw new ArgumentNullException(lastname);

_firstname = firstname;
_lastname
= lastname;
_age
= age;
}

#endregion

#region IFormattable Members

public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null) format = G;

if (formatProvider != null)
{
ICustomFormatter formatter
= formatProvider.GetFormat(
this.GetType())
as ICustomFormatter;

if (formatter != null)
return formatter.Format(format, this, formatProvider);
}

switch(format)
{
case f : return _firstname;
case l : return _lastname;
case na : return string.Format({0} {1} Age= {2},

                        _firstname, _lastname, _age.ToString());
case n :
case G :
default : return string.Format({0} {1},
                              _firstname, _lastname);
}
}

#endregion

public override string ToString()
{
return ToString(G, null);
}

public string ToString(string format)
{
return ToString(format, null);
}

public string ToString(IFormatProvider formatProvider)
{
return ToString(null, formatProvider);
}
}

public class TestClass
{
public static void Main()
{
Person p
= new Person(David,Hayden,99);
Console.WriteLine(
{0}, p); // Outputs “David Hayden”
Console.WriteLine({0:f}, p); // Outputs “David”
Console.WriteLine({0:l}, p); // Outputs “Hayden”
Console.WriteLine({0:n}, p); // Outputs “David Hayden”
Console.WriteLine({0:na}, p); // Outputs “David Hayden, Age=99″
Console.ReadLine();
}
}


The code is fairly straight forward.  Let’s forget about formatProvider for this post and just focus on the switch(format) statement.  The statment essentially just returns a different result based on the value of format.  Format “G” is used by the .NET framework, so just include it for completeness.

This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to Overriding System.Object.ToString() and Implementing IFormattable

  1. David Hayden says:

    You’re absolutely right, Jeremy. In this case, the code would never execute.

    However, for some of your classes, you may implement ICustomFormatter, which is why I always have the check in there. Basically I am just future proofing that method so that if later on I do implement ICustomFormatter it will work as expected and pass control to the ICustomFormatter interface.

    Great question!

  2. Jeremy Griffith says:

    David, thanks for your explanation concerning implementing IFormattable. I have a question.

    Can you explain the following aspect of your code….

    ICustomFormatter formatter = formatProvider.GetFormat(

    this.GetType())

    as ICustomFormatter;

    this.GetType() would always return a type of Person

    your Person type does not implement ICustomFormatter therefore the formatter variable is always going to equal null…

    so what is the point of what follows next…

    if (formatter != null)

    return formatter.Format(format, this, formatProvider);

    the above line of code would never execute…

    am I misunderstanding something?

  3. Check out this article "FormattableObject an Aggregating reflection-based ToString() implementation" by Scott Hanselman

    : http://www.hanselman.com/blog/PermaLink,guid,2a0fdc2c-6b15-4a46-a802-0ebc0b8662d9.aspx

  4. Brendan, this is why in my post about MSIL, I mentioned that when you understand what your code is doing in MSIL, you write better code :) If you look at the IL, you’ll see the boxing instruction for the _age without the ToString(). I am actually working on a post on boxing and unboxing to go up late next week after the final OOP polymorphism post.

  5. I do way too much skimming when I read blogs *blush*

    "If I just pass in "_age", I have to deal with issues of boxing."

    I didn’t know this. I need to learn more about boxing and it’s preformance issues. Perhaps a post on boxing would be a good one for a CodeBetter blogger to tackle..

  6. David Hayden says:

    Hey Brendan,

    In the example above I overloaded ToString a couple of times for flexibility. You can use the overloaded method ToString(string format) to format the person using a given format string.

    You could use attributes to help generate documentation. I typically add this type of information in comments so NDoc can add it to the documentation. If you want something during runtime, a possible solution would be to have the "default" in the switch statement as well as the override of ToString() output a list of possible format strings as well as a description of each. You could certainly just reflect on the attributes at runtime.

    String.Format is expecting an Object and _age is a value type. If I just pass in "_age", I have to deal with issues of boxing. Better to pass "_age.ToString()" beforehand and you save the performance hit.

  7. This is cool David! Nice explanation.

    I use format strings all the time and love them… I would do the extra work of overriding ToString, if I could use them along with a custom format string. I guess I could just try formatting with the custom string if I don’t get one of the expected values, or if I find curly braces. Would that be how you would do it?

    Also, I wonder if there would be a clever way, using attributes or something, where you could be more specific about what values you accept in your ToString. I’ve always hated this… I mean, "G", we use it all the time, but what’s that all about? How about something more strongly typed?

    Not being nitpicky, but in case you missed this, you don’t have to ToString() any of your non-string obejcts passed to String.Format, so this works just as well, and less typing:

    string.Format("{0} {1} Age= {2}",_firstname,_lastname,_age);

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>