3

The values C, E, F, G, X are among the standard format strings. I would like to add another standard string...perhaps the letter 'M' to expand my currency-formatting options. I've made a MoneyFormatInfo class that implements the necessary IFormatProvider and ICustomFormatter interfaces. It works in every scenario I can concoct, except this one...

decimal cash = 3124.728m;

//Code '392' is JAPANESE YEN, with basic French formatting.
var frenchmen = new MoneyFormatInfo("392", new CultureInfo("fr-FR"));

result = cash.ToString("m", frenchmen);
Assert.AreEqual(result, "3 124,73 JPY");

The error message I get is "FormatException was unhandled by user code".

I've reflected the BCL ToString method. I see it consults only the list of standard format strings; I don't see any hook point that would have allowed me to address this. Am I missing something?

Here are other examples that are currently working as expected...

//Code '978' is the Euro
//The custom "Money" class holds an amount and currency type which
//intentionally cannot be overridden.
Money dough = new Money(8124.348m, "978");
decimal cash = 3124.728m;

string result;

//EURO currency parameters, with basic French formatting
var french = new CultureInfo("fr-FR");
result = String.Format(french, "the money: {0:m}", dough);
Assert.AreEqual(result, "the money: 8 124,35 EUR");

//JAPANESE YEN, with basic French formatting.
var frenchmen = new MoneyFormatInfo("392", new CultureInfo("fr-FR"));
result = String.Format(frenchmen, "the cash: {0:m}", cash);
Assert.AreEqual(result, "the cash: 3 124,73 JPY");

result = dough.ToString("c", frenchmen);
Assert.AreEqual(result, "8 124,35 €");

My custom Money class has a ToString() override which performs state-changes, and also converts the 'M' format string into 'C'. In short, it works because I have control over the ToString() method. On the BCL decimal type, I do not have control over the ToString() method. I also do not want to make a custom decimal type.

6
  • Can you provide a source code for the MoneyFormatInfo? I think the problem is there. Commented Jan 12, 2015 at 16:59
  • Can you show an example of what does work? How about string.Format("{0:m}", cash)? Commented Jan 12, 2015 at 17:03
  • Do you see any documentation suggesting that it's possible to add format strings to pre-defined types? According to MSDN (msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx) You're getting the expected behavior. Any character other than the pre-defined values for ToString will throw a FormatException. Commented Jan 12, 2015 at 17:10
  • Are you returning a correct NumberFormatInfo in your IFormatProvider.GetFormat() implementation? That's what decimal.ToString() uses Commented Jan 12, 2015 at 17:13
  • @Jcl: I do return the correct NumberFormatInfo...but it is "too late", because the decimal ToString() has already exploded on my "m" format specifier before any of that can participate. The casing is not an isse...my code always converts to lower-case before proceeding. :) Commented Jan 12, 2015 at 17:18

2 Answers 2

1

I think you can customize how the standard formats come out, but not implement new standard format characters.

class Program
{
   static void Main(string[] args)
   {
      decimal cash = 3124.728m;
      Console.WriteLine("Result: {0}", cash.ToString("C",
         new MoneyFormatInfo("JPY", new System.Globalization.CultureInfo("fr-FR"))));
   }
}

class MoneyFormatInfo : IFormatProvider
{
   System.Globalization.NumberFormatInfo numberFormat;

   public MoneyFormatInfo(string currencyCode, System.Globalization.CultureInfo culture)
   {
      numberFormat = culture.NumberFormat;
      numberFormat.CurrencySymbol = currencyCode;
   }

   public object GetFormat(Type formatType)
   {
      return numberFormat;
   }
}

Notice that you still would use the "C" format code to format a currency value, but you can control what the currency symbol is with your format provider.

Sign up to request clarification or add additional context in comments.

1 Comment

This is right. Somehow I had read the question completely wrong till the latest edit (where I realized I had read it wrong), my comments would have been misleading and I have deleted them
-1

Let check what code you can read easily

result = cash.ToString("m");

or

result = cash.ToFrenchCurrencyString();

Any developer (even you after while) who reads first one example will spend fair amount of time to undestand what this code do and how. The second one is way more easy to undestand and you can just hit F12 to see it's source code. So, I suppose it's more of use to make extension method and put your formating logic inside it.

4 Comments

By being able to hook in to the string format specifiers, it means you could do, for example, String.Format("Price: {0:m}", cash) which is what I believe the OP is after. I could be mistaken...
Yep and that one is harder to undestand as well (:
If in a given set of code I understood m to mean "French Currency Conversion", I'd rather see that than String.Format("Price: {0}", cash.ToFrenchCurrencyString()). You could extend this example to something like logging, where the arguments to String.Format aren't expanded if the logging level isn't at the right level. If the conversion (ie calling ToFrenchCurrencyString) is computationally expensive, you wouldn't want to run it unless you had to.
With high perfomance applications this is could have sense. One should use my method wih considirations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.