在.NET中使用十进制类型的问题

时间:2023-02-05 19:19:11

I'm having a bit of a curious problem with .NET and working with the decimal type. My web application uses the following globalization settings:

我对.NET有一点奇怪的问题,并使用十进制类型。我的Web应用程序使用以下全球化设置:

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

With that in mind, consider the following: the decimal value 123.00 becomes £123.00 in GBP, and EUR 123,00 (note the comma). The problem I'm having appears when I want to go from a decimal value of 123,00 (EUR) back to GBP again, as it becomes 123,000 - which is a huge problem.

考虑到这一点,请考虑以下因素:十进制值123.00英镑为123.00英镑,欧元为123,00(注意逗号)。当我想从123,000(EUR)的十进制值再次回到GBP时,我出现的问题就变成了123,000 - 这是一个很大的问题。

In this scenario, my application has consults a look up table based on the detected culture and shows prices in that currency. The problem only arises when the user has made a selection and the data is prepared for sending to our payment gateway, which convers the EUR 123,00 to 123,000.

在这种情况下,我的应用程序根据检测到的文化查询查询表并显示该货币的价格。只有当用户做出选择并且数据准备好发送到我们的支付网关时才会出现问题,支付网关将123,00欧元转换为123,000。

Any ideas how I can overcome this?

我有什么想法可以克服这个问题?

4 个解决方案

#1


The decimal data type basically just holds a number--there is no formatting implicitly applied to it. The value of a number won't change just because it's being displayed in a different format.

十进制数据类型基本上只包含一个数字 - 没有隐式应用的格式。数字的值不会因为它以不同的格式显示而改变。

Likely, the problem that you're likely having is when you receive the input as a string from a web page, you are parsing it incorrectly.

可能,您可能遇到的问题是当您从网页接收输入为字符串时,您正在解析它。

#2


Thanks to everyone who offered their experience and suggestions.

感谢所有提供经验和建议的人。

Implementing a custom-type/structure for holding my own Decimal values wasn't really what I was after, and wouldn't really give me the functionality I needed (I can already display decimal values in the appropriate local currency/format, I just couldn't convert them back into UK format when required).

实现一个自定义类型/结构来保存我自己的十进制值并不是我真正想要的,并且不会真正给我我需要的功能(我已经可以以适当的本地货币/格式显示十进制值,我只是在需要时无法将它们转换回英国格式)。

The problem was this line in the web config:

问题是网络配置中的这一行:

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

Setting culture = "auto" was allowing .NET to set the locale according to the values provided by the browser (incidentally, by the way, 'enableClientBasedCulture' is not implemented, according to MSDN - so you can omit it). Hence, if a visitor from France (with language 'fr-FR' configured in their browser) visited our site, all the number formatting would work perfectly (correct decimal separator and currency symbol) but I'd have a problem later when trying to conver that number from it's European format to the UK/US format I required.

设置culture =“auto”允许.NET根据浏览器提供的值设置语言环境(顺便提一下,根据MSDN,'enableClientBasedCulture'没有实现 - 所以你可以省略它)。因此,如果来自法国的访问者(在他们的浏览器中配置了语言'fr-FR')访问我们的网站,所有数字格式将完美地工作(正确的小数点分隔符和货币符号)但是我在以后尝试时遇到问题将这个数字从它的欧洲格式转换为我要求的英国/美国格式。

It's odd, but converting "123.00" to the 'fr-FR' locale produces a FormatException because "123.00" is not valid in the French locale (it expects "123,00"). But, converting "123,00" (fr-FR) to the UK/US 'en-GB' or 'en-US' format does NOT produce an error, but instead the value becomes "123,000". I believe this should throw a FormatException because it is not acceptable to add another zero.

这很奇怪,但将“123.00”转换为“fr-FR”语言环境会产生FormatException,因为“123.00”在法语语言环境中无效(它需要“123,00”)。但是,将“123,00”(fr-FR)转换为英国/美国“en-GB”或“en-US”格式不会产生错误,而是将值变为“123,000”。我相信这应该抛出一个FormatException,因为添加另一个零是不可接受的。

The solution I implemented was as follows:

我实施的解决方案如下:

  1. Set culture="auto" to culture="en-GB" in web.config.
  2. 在web.config中将culture =“auto”设置为culture =“en-GB”。

  3. Use Decimal.ToString("c", ni) - where 'ni' is custom NumberFormatInfo class.
  4. 使用Decimal.ToString(“c”,ni) - 其中'ni'是自定义NumberFormatInfo类。

Since my existing code connects to our data source to retrieve the correct decimal values dependant on country, all I had now was a formatting issue. So, to format a number according to the 'fr-FR' locale, you can do:

由于我现有的代码连接到我们的数据源以检索依赖于国家/地区的正确小数值,因此我现在所有的都是格式化问题。因此,要根据'fr-FR'区域设置格式化数字,您可以执行以下操作:

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

In this setup, all my decimal values (internally) are always treated as en-GB decimals, and thus in the format I require. In other words, my application did not require the flexibility of being able to change the settings that apply to the entire current thread; rather just the opposite: I only cared about formatting the values differently.

在此设置中,我的所有十进制值(内部)始终被视为en-GB小数,因此采用我需要的格式。换句话说,我的应用程序不需要能够灵活地更改适用于整个当前线程的设置;恰恰相反:我只关心不同地格式化值。

#3


Maybe this could help you

也许这可以帮到你

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

usage i.e.

        Double.Parse(MyString, ni)

-rAyt

Don't know any better way to detect the format, but take a look at this

不知道有什么更好的方法来检测格式,但看看这个

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

#4


rAyt has offered a good start, but if you are working with a variety of cultures, it could become very difficult to automatically detect which culture a decimal number is. To ensure that you always know, it might be best to create a simple wrapper type around decimal that includes the culture. You would need to update your data store to also store the culture code (i.e. en-US) so that when reconstituting data you don't "forget" what culture the data was saved as. Any entities you have would use your decimal wrapper type, rather than decimal directly, and that type could have an overridden ToString method to automatically render in the correct format:

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
}

#1


The decimal data type basically just holds a number--there is no formatting implicitly applied to it. The value of a number won't change just because it's being displayed in a different format.

十进制数据类型基本上只包含一个数字 - 没有隐式应用的格式。数字的值不会因为它以不同的格式显示而改变。

Likely, the problem that you're likely having is when you receive the input as a string from a web page, you are parsing it incorrectly.

可能,您可能遇到的问题是当您从网页接收输入为字符串时,您正在解析它。

#2


Thanks to everyone who offered their experience and suggestions.

感谢所有提供经验和建议的人。

Implementing a custom-type/structure for holding my own Decimal values wasn't really what I was after, and wouldn't really give me the functionality I needed (I can already display decimal values in the appropriate local currency/format, I just couldn't convert them back into UK format when required).

实现一个自定义类型/结构来保存我自己的十进制值并不是我真正想要的,并且不会真正给我我需要的功能(我已经可以以适当的本地货币/格式显示十进制值,我只是在需要时无法将它们转换回英国格式)。

The problem was this line in the web config:

问题是网络配置中的这一行:

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

Setting culture = "auto" was allowing .NET to set the locale according to the values provided by the browser (incidentally, by the way, 'enableClientBasedCulture' is not implemented, according to MSDN - so you can omit it). Hence, if a visitor from France (with language 'fr-FR' configured in their browser) visited our site, all the number formatting would work perfectly (correct decimal separator and currency symbol) but I'd have a problem later when trying to conver that number from it's European format to the UK/US format I required.

设置culture =“auto”允许.NET根据浏览器提供的值设置语言环境(顺便提一下,根据MSDN,'enableClientBasedCulture'没有实现 - 所以你可以省略它)。因此,如果来自法国的访问者(在他们的浏览器中配置了语言'fr-FR')访问我们的网站,所有数字格式将完美地工作(正确的小数点分隔符和货币符号)但是我在以后尝试时遇到问题将这个数字从它的欧洲格式转换为我要求的英国/美国格式。

It's odd, but converting "123.00" to the 'fr-FR' locale produces a FormatException because "123.00" is not valid in the French locale (it expects "123,00"). But, converting "123,00" (fr-FR) to the UK/US 'en-GB' or 'en-US' format does NOT produce an error, but instead the value becomes "123,000". I believe this should throw a FormatException because it is not acceptable to add another zero.

这很奇怪,但将“123.00”转换为“fr-FR”语言环境会产生FormatException,因为“123.00”在法语语言环境中无效(它需要“123,00”)。但是,将“123,00”(fr-FR)转换为英国/美国“en-GB”或“en-US”格式不会产生错误,而是将值变为“123,000”。我相信这应该抛出一个FormatException,因为添加另一个零是不可接受的。

The solution I implemented was as follows:

我实施的解决方案如下:

  1. Set culture="auto" to culture="en-GB" in web.config.
  2. 在web.config中将culture =“auto”设置为culture =“en-GB”。

  3. Use Decimal.ToString("c", ni) - where 'ni' is custom NumberFormatInfo class.
  4. 使用Decimal.ToString(“c”,ni) - 其中'ni'是自定义NumberFormatInfo类。

Since my existing code connects to our data source to retrieve the correct decimal values dependant on country, all I had now was a formatting issue. So, to format a number according to the 'fr-FR' locale, you can do:

由于我现有的代码连接到我们的数据源以检索依赖于国家/地区的正确小数值,因此我现在所有的都是格式化问题。因此,要根据'fr-FR'区域设置格式化数字,您可以执行以下操作:

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

In this setup, all my decimal values (internally) are always treated as en-GB decimals, and thus in the format I require. In other words, my application did not require the flexibility of being able to change the settings that apply to the entire current thread; rather just the opposite: I only cared about formatting the values differently.

在此设置中,我的所有十进制值(内部)始终被视为en-GB小数,因此采用我需要的格式。换句话说,我的应用程序不需要能够灵活地更改适用于整个当前线程的设置;恰恰相反:我只关心不同地格式化值。

#3


Maybe this could help you

也许这可以帮到你

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

usage i.e.

        Double.Parse(MyString, ni)

-rAyt

Don't know any better way to detect the format, but take a look at this

不知道有什么更好的方法来检测格式,但看看这个

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

#4


rAyt has offered a good start, but if you are working with a variety of cultures, it could become very difficult to automatically detect which culture a decimal number is. To ensure that you always know, it might be best to create a simple wrapper type around decimal that includes the culture. You would need to update your data store to also store the culture code (i.e. en-US) so that when reconstituting data you don't "forget" what culture the data was saved as. Any entities you have would use your decimal wrapper type, rather than decimal directly, and that type could have an overridden ToString method to automatically render in the correct format:

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
}