Home > Articles > Programming > .NET and Windows Programming

C# 4.0 How-To: Creating Versatile Types

Ben Watson
  • By Ben Watson
  • Mar 10, 2010
  • Sample Chapter is provided courtesy of Sams Publishing
  • PrintPrint
  • Share ThisShare This
  • DiscussDiscuss
C# 4.0 How-To

This chapter is from the book
C# 4.0 How-To

This chapter is all about making your own objects as useful and versatile as possible. In many cases, this means implementing the standard interfaces that .NET provides or simply overriding base class methods.

Whenever you create your own classes, you need to consider the circumstances under which they could be used. For example, will two instances of your Item struct ever be compared for equality? Will your Person class need to be serializable, or sortable?

This chapter is all about making your own objects as useful and versatile as possible. In many cases, this means implementing the standard interfaces that .NET provides or simply overriding base class methods.

Format a Type with ToString()

Scenario/Problem:

You need to provide a string representation of an object for output and debugging purposes.

Solution:

By default, ToString() will display the type’s name. To show your own values, you must override the method with one of your own. To illustrate this, let’s continue our Vertex3d class example from the previous chapter.

Assume the class initially looks like this:

struct Vertex3d
{
    private double _x;
    private double _y;
    private double _z;

    public double X
    {
        get { return _x; }
        set { _x = value; }
    }

    public double Y
    {
        get { return _y; }
        set { _y = value; }
    }
    public double Z
    {
        get { return _z; }
        set { _z = value; }
    }

    public Vertex3d(double x, double y, double z)
    {
        this._x = x;
        this._y = y;
        this._z = z;
    }
}

Override ToString() for Simple Output

To get a simple string representation of the vertex, override ToString() to return a string of your choosing.

public override string ToString()
{
    return string.Format("({0}, {1}, {2})", X, Y, Z);
}

The code

Vertex3d v = new Vertex3d(1.0, 2.0, 3.0);
Trace.WriteLine(v.ToString());

produces the following output:

(1, 2, 3)

Implement Custom Formatting for Fine Control

Scenario/Problem:

You need to provide consumers of your class fine-grained control over how string representations of your class look.

Solution:

Although the ToString() implementation gets the job done, and is especially handy for debugging (Visual Studio will automatically call ToString() on objects in the debugger windows), it is not very flexible. By implementing IFormattable on your type, you can create a version of ToString() that is as flexible as you need.

Let’s create a simple format syntax that allows us to specify which of the three values to print. To do this, we’ll define the following format string:

"X, Y"

This tells Vertex3d to print out X and Y. The comma and space (and any other character) will be output as-is.

The struct definition will now be as follows:

using System;
using System.Collections.Generic;
using System.Text;

namespace VertexDemo
{
struct Vertex3d : IFormattable
{
    ...
    public string ToString(string format, IFormatProvider formatProvider)
    {
        //"G" is .Net's standard for general formatting--all
        //types should support it
        if (format == null) format = "G";

        // is the user providing their own format provider?
        if (formatProvider != null)
        {
            ICustomFormatter formatter =
                formatProvider.GetFormat(this.GetType())
                        as ICustomFormatter;
            if (formatter != null)
            {
                return formatter.Format(format, this, formatProvider);
            }
        }

        //formatting is up to us, so let's do it
        if (format == "G")
        {
            return string.Format("({0}, {1}, {2})", X, Y, Z);
        }

        StringBuilder sb = new StringBuilder();
        int sourceIndex = 0;
        while (sourceIndex < format.Length)
        {
            switch (format[sourceIndex])
            {
                case 'X':
                    sb.Append(X.ToString());
                    break;
                case 'Y':
                    sb.Append(Y.ToString());
                    break;
                case 'Z':
                    sb.Append(Z.ToString());
                    break;
                default:
                    sb.Append(format[sourceIndex]);
                    break;
            }
            sourceIndex++;
        }
        return sb.ToString();
    }
}
}

The formatProvider argument allows you to pass in a formatter that does something different from the type’s own formatting (say, if you can’t change the implementation of ToString() on Vertex3d for some reason, or you need to apply different formatting in specific situations). You’ll see how to define a custom formatter in the next section.

Formatting with ICustomFormatter and StringBuilder

Scenario/Problem:

You need a general-purpose formatter than can apply custom formats to many types of objects.

Solution:

Use ICustomFormatter and StringBuilder. This example prints out type information, as well as whatever the custom format string specifies for the given types.

class TypeFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter)) return this;
        return Thread.CurrentThread.CurrentCulture.GetFormat(formatType);
    }

    public string Format(string format, object arg, IFormatProvider
formatProvider)
    {
        string value;
        IFormattable formattable = arg as IFormattable;
        if (formattable == null)
        {
            value = arg.ToString();
        }
        else
        {
            value = formattable.ToString(format, formatProvider);
        }
        return string.Format("Type: {0}, Value: {1}", arg.GetType(),
value);
    }
}

The class can be used like this:

Vertex3d v = new Vertex3d(1.0, 2.0, 3.0);
Vertex3d v2 = new Vertex3d(4.0, 5.0, 6.0);
TypeFormatter formatter = new TypeFormatter();
StringBuilder sb = new StringBuilder();
sb.AppendFormat(formatter, "{0:(X Y)}; {1:[X, Y, Z]}", v, v2);
Console.WriteLine(sb.ToString());

The following output is produced:

Type: ch02.Vertex3d, Value: (1 2); Type: ch02.Vertex3d, Value: [4, 5, 6]
  • Share ThisShare This
  • Your Account

Discussions

Make a New Comment

You must log in in order to post a comment.

Related Resources

 Big Nerd RanchAsk Big Nerd Ranch: Blocks in Objective-C
By Big Nerd Ranch on June 24, 2010 No Comments

Adam Preble answers a question about blocks.

Danny KalevYves Smith: Suspicions that The Fed is manipulating Wall Street
By Danny Kalev on May 24, 2010 No Comments

Yves Smith, the nom de plume of the creator of Naked Capitalism and one of the most savvy and respected members of the blogosphere. In professional life Yves is known as Susan Webber. Yves recently gave an interview to an Israeli financial newspaper in which she claims that a federal team unofficially called "the plunge protection team" is manipulating the stocks on Wall Street.

 Big Nerd RanchAsk Big Nerd Ranch: Rotating an iPhone View Around a Point
By Big Nerd Ranch on May 20, 2010 No Comments

Brian Hardy answers a question about view rotation.

Sample code for this article is available in the Big Nerd Ranch github repository. The sample application demonstrates several techniques illustrated here, and works on iPhone or iPad.

Q. On iPhone OS, how can I rotate a view around an arbitrary point?

A. By default, views in Cocoa Touch (and Cocoa) are configured to rotate around their center point. While this is commonly useful (think of a UIActivityIndicatorView), often you will want to use a point other than the center. There are (at least) two ways of doing this. You can change the anchorPoint property of the view's layer. Alternatively, you can wrap the view in a superview, with the superview's center located at the point you want to rotate around. In either case, the mechanism for rotation is the same. Both techniques are discussed here.

See All Related Blogs

Informit Network