Проблемы работы с десятичным типом в .NET - PullRequest
4 голосов
/ 22 мая 2009

У меня возникла любопытная проблема с .NET и я работаю с десятичным типом. Мое веб-приложение использует следующие параметры глобализации:

<globalization culture="auto" enableClientBasedCulture="true"/>

Имея это в виду, учтите следующее: десятичное значение 123,00 становится £ 123,00 в фунтах стерлингов, а EUR 123,00 (обратите внимание на запятую). Возникающая у меня проблема возникает, когда я хочу снова перейти от десятичного значения 123,00 (EUR) к фунту стерлингов, поскольку он становится 123,000 - что является огромной проблемой.

В этом сценарии мое приложение имеет справочную таблицу на основе обнаруженной культуры и показывает цены в этой валюте. Проблема возникает только тогда, когда пользователь сделал выбор и данные готовы для отправки на наш платежный шлюз, который запрашивает от 123,00 до 123 000 евро.

Есть идеи, как мне это преодолеть?

Ответы [ 4 ]

1 голос
/ 26 мая 2009

Спасибо всем, кто предложил свой опыт и предложения.

Реализация пользовательского типа / структуры для хранения моих собственных десятичных значений была не совсем тем, что мне было нужно, и не дала бы мне необходимой функциональности (я уже могу отображать десятичные значения в соответствующей местной валюте / формате). Я просто не смог преобразовать их обратно в британский формат при необходимости).

Проблема была в этой строке в веб-конфигурации:

<globalization culture="auto" enableClientBasedCulture="true"/>

Установка culture = "auto" позволяла .NET устанавливать языковой стандарт в соответствии со значениями, предоставленными браузером (между прочим, кстати, enableClientBasedCulture не реализован, в соответствии с MSDN - так что вы можете его опустить). Следовательно, если посетитель из Франции (с языком 'fr-FR', настроенным в их браузере) посетит наш сайт, все форматирование чисел будет работать идеально (правильный десятичный разделитель и символ валюты), но у меня возникнет проблема позже при попытке преобразовать это число из европейского формата в нужный мне британский / американский формат.

Это странно, но преобразование «123.00» в локаль «fr-FR» создает исключение FormatException, поскольку «123.00» недопустимо во французской локали (ожидается «123,00»). Но преобразование «123,00» (франк-франк) в британский / американский формат «en-GB» или «en-US» НЕ приводит к ошибке, вместо этого значение становится «123,000». Я считаю, что это должно вызвать исключение FormatException, потому что нельзя допускать добавление еще одного нуля.

Решение, которое я реализовал, было следующим:

  1. Установите значение culture = "auto" для culture = "en-GB" в web.config.
  2. Использовать Decimal.ToString ("c", ni) - где 'ni' - это собственный класс NumberFormatInfo.

Поскольку мой существующий код подключается к нашему источнику данных для получения правильных десятичных значений в зависимости от страны, у меня теперь была только проблема с форматированием. Таким образом, чтобы отформатировать число в соответствии с локалью 'fr-FR', вы можете сделать:

NumberFormatInfo ni = Globalization.CultureInfo.GetCultureInfo("fr-FR").NumberFormat;
Decimal.ToString("c", ni);

В этой настройке все мои десятичные значения (внутренне) всегда обрабатываются как десятичные дроби en-GB, и, следовательно, в требуемом формате. Другими словами, мое приложение не требовало гибкости, чтобы иметь возможность изменять настройки, которые применяются ко всему текущему потоку; скорее наоборот: я заботился только о форматировании значений по-разному.

1 голос
/ 22 мая 2009

Десятичный тип данных в основном просто содержит число - к нему неявно применяется форматирование. Значение числа не изменится только потому, что оно отображается в другом формате.

Вероятно, проблема, с которой вы, вероятно, сталкиваетесь, заключается в том, что когда вы получаете ввод в виде строки с веб-страницы, вы неправильно ее анализируете.

1 голос
/ 22 мая 2009

Может быть, это может помочь вам

        NumberFormatInfo ni = new NumberFormatInfo();
        ni.NumberDecimalSeparator = '.';

использование, т.е.

        Double.Parse(MyString, ni)

-rAyt

Не знаю лучшего способа определить формат, но взгляните на это

    static void Main(string[] args)
    {
        // German Format (EUR)
        CultureInfo ci_de = new CultureInfo("de-DE");

        // English Format (GBP)
        CultureInfo ci_gb = new CultureInfo("en-GB");


        string test_de = "1.234.567.890,12";
        string test_en = "1,234,567,890.12";

        double result_de = 0.0;
        double result_en = 0.0;

        try
        {
            result_de = Double.Parse(test_en, ci_de.NumberFormat);
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Number isn't a german format " + ex.InnerException);
        }
        try
        {
            result_en = Double.Parse(test_en, ci_gb.NumberFormat);
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Number isn't a english format " + ex.InnerException);
        }

        Console.WriteLine(result_de.ToString());
        Console.WriteLine(result_en.ToString());

        Console.ReadLine();
    }
0 голосов
/ 23 мая 2009

rAyt предложил хорошее начало, но если вы работаете с различными культурами, может быть очень трудно автоматически определить, какой культурой является десятичное число. Чтобы вы всегда знали, может быть лучше создать простой тип обёртки вокруг десятичной дроби, включающий в себя культуру. Вам необходимо обновить хранилище данных, чтобы оно также сохраняло код культуры (например, en-US), чтобы при восстановлении данных вы не «забыли», в какой культуре были сохранены данные. Любые объекты, которые у вас есть, будут использовать ваш тип десятичной обертки, а не непосредственно десятичный, и этот тип может иметь переопределенный метод ToString для автоматической визуализации в правильном формате:

public struct CulturedDecimal
{
    public decimal Value;
    public CultureInfo Culture;

    public override ToString()
    {
        return Value.ToString(Culture);
    }
}

public class SomeEntity
{
    public int ID { get; set; }
    public CulturedDecimal MonetaryValue { get; set; } // Was 'decimal' before
}
...