5

I have the following two lines of code:

var BadResult = (100).ToString("B", new CustomFormatter ());
var GoodResult = String.Format("{0}", 100, new CustomFormatter ());

Whereas, BadResult obviously is bad, and GoodResult is good. My CustomFormatter class is declared like this: (also, with the one function I feel is relevant):

public class CustomFormatter 
               : IFormatProvider, ICustomFormatter
{
    public virtual Object GetFormat(Type formatType)
    {
        String formatTypeName = formatType.ToString();
        formatTypeName = formatTypeName;
        Object formatter = null;
        if (formatType == typeof(ICustomFormatter))
            formatter = this;
        return formatter;
    }
}

The issue itself, when I run the line of code with "good result", the GetFormat function is requestng an instance of CustomFormatter.

Whenever its called with Float.Tostring(), its expecting an instance of NumberFormatInfo.

I initially jumped to "my CustomFormatter should be deriving from NumberFormatInfo". Unfortunately, the class is sealed.

So: What do I need to do to be able to call Float.ToString() with a custom formatter?

Thanks!

greggorob64
  • 2,487
  • 2
  • 27
  • 55

2 Answers2

3

Your

 var GoodResult = String.Format("{0}", 100, new CustomFormatter ());

is not using the CustomFormatter. So your good results seems to be achieved by the defaults.

What you want is probably:

 var GoodResult = String.Format(new CustomFormatter (), "{0}", 100);

See how that works.

H H
  • 263,252
  • 30
  • 330
  • 514
  • I need (100.0).ToString("{0}", new CustomFormatter()); to work. I have String.Format working fine already. – greggorob64 Mar 26 '12 at 14:07
  • That is not possible. The only reason it takes an IFormatProvider is to allow passing CultureInfo. Which it uses the NumberFormatInfo inside of the CultureInfo anyways. So the only type Int.ToString can really accept is NumberFormatInfo. – Will Mar 26 '12 at 14:22
1

I'm not sure you can use a custom formatter with Number.ToString. All the examples I've seen with custom formatters use String.Format (this on the MSDN for instance).

I suggest you to try an extension method:

public static class MyExt 
{
    public static string ToFormattedString(this float This, string format, IFormatProvider provider)
    {
        return String.Format(provider,"{0}", new object[] {This});
    }
}

//now this works
var NoLongerBadResult = (100F).ToFormattedString("B", new CustomFormatter ());

EDIT ok, I think I got it. You need to change the current NumberFormatInfo and return it from GetFormat:

public class CustomFormatter :  IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else if(formatType == typeof(NumberFormatInfo))
        {
            NumberFormatInfo nfi = (NumberFormatInfo)NumberFormatInfo.CurrentInfo.Clone(); // create a copy of the current NumberFormatInfo
            nfi.CurrencySymbol = "Foo"; // change the currency symbol to "Foo" (for instance)
            return nfi; // and return our clone
        }
        else
            return null;
    }

    public string Format(string fmt, object arg, IFormatProvider formatProvider)
    {
        return "test";
    }
}

now this works:

var NowItWorks = (100).ToString("C", new CustomFormatter ());
var GoodResult = String.Format(new CustomFormatter (),"{0}", 100);
Console.WriteLine(NowItWorks); // Foo 100.00
Console.WriteLine(GoodResult); // test
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • From my internet-detective work this is starting to seem like the solution. However, why the hell is the a float.tostring() overload that allows you to give an icustomformatter? – greggorob64 Mar 26 '12 at 14:31
  • @greggorob64 you're right that it should be possible. The documentation for [Single.ToString](http://msdn.microsoft.com/en-us/library/6dx8etks.aspx) says "The provider parameter is an IFormatProvider implementation whose GetFormat method returns a NumberFormatInfo object", so it seems that there should be a way to create a NumberFormatInfo object and return it from your custom getformat. I have not found a way to do it, though... But I'm still trying – Paolo Falabella Mar 26 '12 at 14:43
  • 1
    @greggorob64 ok, so... Your custom provider works, but if you use it with Number.Tostring you are restricted to using the formats that NumberFormatProvider understands, so B does not work (but if you try `(100).ToString("C", new CustomFormatter ());` it works and 100 gets formatted as a currency). You can probably return a custom NumberFormatProvider, where you change the symbol for the decimal point, for instance, but you are restricted to the type of changes that NumberFormatInfo itself allows you to customize – Paolo Falabella Mar 26 '12 at 14:52
  • Unforunately, NumberFormatInfo is sealed so i don't get any chance at modifying it. – greggorob64 Mar 26 '12 at 14:56
  • you don't need to subclass it and you can modify the a NumberFormatInfo by cloning it first. See the EDIT – Paolo Falabella Mar 26 '12 at 15:18
  • 1
    Correct me if I'm wrong, but this won't allow for a full-blown custom format string (like, for instance, I want to add a "B" formatting key-letter. This will only allow me to modify those items modify-able by the NumberFormatInfo – greggorob64 Mar 26 '12 at 15:46