Saturday, May 19, 2012

using IFormatProvider and ICustomFormatter

On page 224 of "C# 4.0 in a Nutshell" I found some puzzling code. It is given again here. I typed it up and got it working.

using System;
using System.Globalization;
using System.Text;
namespace Whatever.Models
{
   public class WordyFormatProvider : IFormatProvider, ICustomFormatter
   {
      private static readonly string[] _numberWords = "zero one two three four five six seven
            eight night minus point".Split();
      
      private IFormatProvider _parent;
      
      public WordyFormatProvider() : this(CultureInfo.CurrentCulture) {}
      public WordyFormatProvider (IFormatProvider parent)
      {
         _parent = parent;
      }
      
      public object GetFormat (Type formatType)
      {
         if (formatType == typeof(ICustomFormatter)) return this;
         return null;
      }
      
      public string Format (string format, object arg, IFormatProvider prov)
      {
         if (arg == null || format != "W") return string.Format(_parent, "{0:" + format + "}",
               arg);
         StringBuilder result = new StringBuilder();
         string digitList = string.Format(CultureInfo.InvariantCulture, "{0}", arg);
         foreach (char digit in digitList)
         {
            int i = "0123456789-.".IndexOf(digit);
            if (i == -1) continue;
            if (result.Length > 0) result.Append(' ');
            result.Append(_numberWords[i]);
         }
         return result.ToString();
      }
   }
}

 
 

Alright, the GetFormat method isn't really used in the book's example, but Visual Studio will put a red squiggly beneath IFormatProvider in line six above without it. (IFormatProvider requires it.) I didn't realize this until I typed up the code. Also Visual Studio will show a red squiggly below ICustomFormatter if the Format method, which we test below, is not there. (ICustomFormatter requires it.) What does this thing do? It allows you to cast a number to a series of words like so:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Whatever.Models;
namespace Whatever.Tests
{
   [TestClass]
   public class WordyFormatProviderTests
   {
      [TestMethod]
      public void Test()
      {
         double number = -1.5;
         IFormatProvider formatProvider = new WordyFormatProvider();
         string formattedNumber = string.Format(formatProvider, "{0:W}", number);
         Assert.AreEqual(formattedNumber, "minus one point five");
      }
   }
}

 
 

I tried to remove ICustomFormatter from line six of WordyFormatProvider, but then I got this error at line fourteen of WordyFormatProviderTests:

Unable to cast object of type 'Whatever.Models.WordyFormatProvider' to type 'System.ICustomFormatter'.

 
 

It is hard to see the dependency on ICustomFormatter. string.Format apparently needs it associated with an object of IFormatProvider type as shown here. I thought my head was gonna explode.

No comments:

Post a Comment