Добавление строки пользовательского формата к доступным стандартным строкам

значения C, E, F, G, X относятся к строкам стандартного формата. Я хотел бы добавить еще одну стандартную строку... возможно, букву «М», чтобы расширить параметры форматирования валюты. Я создал класс MoneyFormatInfo, реализующий необходимые интерфейсы IFormatProvider и ICustomFormatter. Это работает во всех сценариях, которые я могу придумать, кроме этого...

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");

Сообщение об ошибке, которое я получаю: «FormatException не было обработано кодом пользователя».

Я отразил метод BCL ToString. Я вижу, что он обращается только к списку строк стандартного формата; Я не вижу ни одной зацепки, которая позволила бы мне решить эту проблему. Я что-то упускаю?

Вот другие примеры, которые в настоящее время работают, как ожидалось...

//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 €");

Мой пользовательский класс Money имеет переопределение ToString(), которое выполняет изменения состояния, а также преобразует строку формата «M» в «C». Короче говоря, это работает, потому что я контролирую метод ToString(). В десятичном типе BCL у меня нет контроля над методом ToString(). Я также не хочу делать собственный десятичный тип.


person Brent Arias    schedule 12.01.2015    source источник
comment
Можете ли вы предоставить исходный код для MoneyFormatInfo? Я думаю, что проблема в этом.   -  person VMAtm    schedule 12.01.2015
comment
Можете ли вы показать пример того, что работает? Как насчет string.Format("{0:m}", cash)?   -  person BlueMonkMN    schedule 12.01.2015
comment
Видите ли вы какую-либо документацию, предполагающую возможность добавления строк формата к предопределенным типам? Согласно MSDN (msdn.microsoft.com/en -us/library/dwhawy9k(v=vs.110).aspx) Вы получаете ожидаемое поведение. Любой символ, отличный от предопределенных значений для ToString, вызовет исключение FormatException.   -  person BlueMonkMN    schedule 12.01.2015
comment
Вы возвращаете правильный NumberFormatInfo в своей реализации IFormatProvider.GetFormat()? Это то, что использует decimal.ToString()   -  person Jcl    schedule 12.01.2015
comment
@Jcl: я возвращаю правильный NumberFormatInfo ... но уже слишком поздно, потому что десятичный ToString () уже взорвался в моем спецификаторе формата m, прежде чем что-либо из этого может участвовать. Корпус не является проблемой ... мой код всегда преобразуется в нижний регистр, прежде чем продолжить. :)   -  person Brent Arias    schedule 12.01.2015
comment
Декомпилированный исходный код для этой перегрузки decimal.ToString() в mscorlib гласит: return Number.FormatDecimal(this, format, NumberFormatInfo.GetInstance(provider));, так что я сомневаюсь, что это так. Редактировать: увидел ваше последнее редактирование, неважно.   -  person Jcl    schedule 12.01.2015


Ответы (2)


Я думаю, вы можете настроить вывод стандартных форматов, но не внедрять новые символы стандартного формата.

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;
   }
}

Обратите внимание, что вы по-прежнему будете использовать код формата «C» для форматирования значения валюты, но вы можете управлять символом валюты с помощью поставщика формата.

person BlueMonkMN    schedule 12.01.2015
comment
Это правильно. Каким-то образом я совершенно неправильно прочитал вопрос до последнего редактирования (где я понял, что прочитал его неправильно), мои комментарии могли бы ввести в заблуждение, и я удалил их. - person Jcl; 12.01.2015

Давайте проверим, какой код вы можете легко прочитать

result = cash.ToString("m");

or

result = cash.ToFrenchCurrencyString();

Любой разработчик (даже вы через некоторое время), прочитавший сначала один пример, потратит немало времени, чтобы понять, что этот код делает и как. Второй гораздо проще понять, и вы можете просто нажать F12, чтобы увидеть его исходный код. Итак, я полагаю, что более полезно создать метод расширения и поместить в него свою логику форматирования.

person IAfanasov    schedule 12.01.2015
comment
Возможность подключиться к спецификаторам строкового формата означает, что вы можете сделать, например, String.Format("Price: {0:m}", cash), что, как я полагаю, и нужно OP. могу ошибаться... - person James Thorpe; 12.01.2015
comment
Да и понять его тоже сложнее (: - person IAfanasov; 12.01.2015
comment
Если в заданном наборе кода я понял, что m означает конвертацию французской валюты, я бы предпочел увидеть это, а не String.Format("Price: {0}", cash.ToFrenchCurrencyString()). Вы можете расширить этот пример до чего-то вроде ведения журнала, где аргументы для String.Format не расширяются, если уровень ведения журнала не на правильном уровне. Если преобразование (т. е. вызов ToFrenchCurrencyString) требует значительных вычислительных ресурсов, вы не захотите запускать его, если в этом нет необходимости. - person James Thorpe; 12.01.2015
comment
С высокопроизводительными приложениями это может иметь смысл. Следует использовать мой метод с соображениями. - person IAfanasov; 12.01.2015