String.Format and IFormatProvider


Have you ever noticed this overload of String.Format(IFormatProvider, String, Object[])? IFormatProvider is actually a very useful interface that allows us to create custom formats to transform our inputs. The IFormatProvider link has examples on how to do it.

This is my version on how to create a custom formatter factory that understand custom formats we create, and routes to the appropriate formatter. If a non-custom format is used, the conversion will still be performed as normal. In the example, I’m trying to create custom formats “bsb” and “acctno” which will convert numbers to a “999-999” format and “1234-56789” format using the CustomFormatProvider.

Here is my CustomFormatProvider class.

public class CustomFormatProvider : IFormatProvider, ICustomFormatter
{
    private readonly IStringFormatterFactory _stringFormatterFactory;

    public CustomFormatProvider() : this(new StringFormatterFactory())
    {
    }

    public CustomFormatProvider(IStringFormatterFactory stringFormatterFactory)
    {
        _stringFormatterFactory = stringFormatterFactory;
    }

    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        return null;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        IStringFormatter stringFormatter = _stringFormatterFactory.GetFormatter(format);
        return stringFormatter.Format(arg, format);
    }
}

Here is my StringFormatterFactory

public class StringFormatterFactory : IStringFormatterFactory
{
    public IStringFormatter GetFormatter(string format)
    {
        if (format == CustomStringFormat.Bsb)
            return new BsbStringFormatter();

        if (format == CustomStringFormat.AccountNumber)
            return new AccountNumberStringFormatter();

        return new DefaultStringFormatter();
    }
}

Now here are my StringFormatter implementations for bsb and acctno, and a default one.

public class BsbStringFormatter : IStringFormatter
{
    private const string ValidFormat = "^[0-9]{6}$";

    public string Format(object arg, string format)
    {
        string input = arg.ToString();
        if (Regex.IsMatch(input, ValidFormat))
        {
            return string.Format("{0}-{1}", input.Substring(0, 3), input.Substring(3, 3));
        }
        throw new FormatException(
            string.Format("unable to format input value {0} to {1} format", input, format));
    }
}

public class AccountNumberStringFormatter : IStringFormatter
{
    private const string ValidFormat = "^[0-9]{9}$";

    public string Format(object arg, string format) {
        string input = arg.ToString();
        if (Regex.IsMatch(input, ValidFormat)) {
            return string.Format("{0}-{1}", input.Substring(0, 5), input.Substring(5, 4));
        }
        throw new FormatException(
            string.Format("unable to format input value {0} to {1} format", input, format));
    }
}

public class DefaultStringFormatter : IStringFormatter
{
    public string Format(object arg, string format)
    {
        if (arg is IFormattable)
            return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);

        if (arg != null)
            return arg.ToString();

        return String.Empty;
    }
}

Now this is how one would use the CustomFormatProvider

var customFormatProvider = new CustomFormatProvider();

string message =
    string.Format(customFormatProvider, "BSB:{0:bsb}, ACCT:{1:acctno}, BALANCE:{2:C2}", "013987", "123456789", 450.22);
// will display "BSB:013-987, ACCT:12345-6789, BALANCE:$450.22"

Download code sample here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: