Int32.ToString()方法是否与文化相关?

75

我正在运行ReSharper的beta版本,它为以下代码提供警告:

int id;
// ...
DoSomethingWith(id.ToString());
警告出现在id.ToString()调用上,它告诉我"明确指定一个字符串转换的文化"。我理解这个警告,我知道怎么修复 -- 只需将代码更改为更加繁琐的 id.ToString(CultureInfo.InvariantCulture)

但我的问题是:那是否有必要呢?我的意思是,显然,在使用像DateTime(不同文化有不同的日期格式)和Double(小数点使用不同字符)这样的类型时,指定文化很重要。但是Int32.ToString(),至少在en-US和不变文化中,根本不会添加任何格式。没有逗号、没有小数点、没有美元符号,什么都没有。那么有什么需要根据文化而变化呢?

是否有一些文化实际上在调用无参数的Int32.ToString()时添加某种格式?还是这是 ReSharper beta中的一个错误,这个警告真的不适用于Int32(如果是这样,我会提交一个 ReSharper 的bug报告)?


你使用的是哪个ReSharper版本?据我所知,目前没有任何ReSharper测试版。 - Kiley Naro
@KileyNaro:ReSharper 6.1自11月初以来一直处于EAP状态。 - Joe White
还要注意的是,Visual Studio代码分析(又名FxCop)会对此提出抱怨,并催促您明确设置文化。因此,这并不是Resharper的错。 - Polyfun
@ShellShock,当然可以;ReSharper只是启发了这个问题,但这个问题本身也很有价值,特别是因为答案令人惊讶。 - Joe White
2
6.1 在我们的代码中检测到了很多调用 ToString() 的问题,但似乎对于 string.Format() 并没有做出相同的反应? - Stephen Kennedy
如果是用于显示,逻辑上你会将它更改为 id.ToString(CultureInfo.CurrentCulture)。我建议为所有你关心的类型定义两个扩展方法:ToDisplayString()ToInvariantString()。然后不再考虑你拥有的类型,只考虑你正在创建字符串的目的。自记录代码。 - ToolmakerSteve
7个回答

76

操作系统允许更改数字的负号

Control panel -> 
   Language and regional settings -> 
         Additional settings -> 
             Negative sign

因此,当前文化可能已经覆盖了负号。在这种情况下,您需要尊重区域设置,这是警告的原因。您还可以通过编程方式更改负号:

    CultureInfo culture = Thread.CurrentThread.CurrentCulture;
    // Make a writable clone
    culture = (CultureInfo) culture.Clone();
    culture.NumberFormat.NegativeSign = "!";

3
你能给一个实行这种方式的文化例子吗? - parapura rajkumar
你可以在操作系统设置中更改它。 - Daniel Peñalba
也许一些亚洲文化使用双宽减号? - Stefan Paul Noack
22
我不确定是否有任何文化使用不同的负号,但是有一次客户报告说我们的产品无法解析负数,让我吃惊的是,这个疯狂的人用"!"来代替负号。为什么呢?我不知道。 - Daniel Peñalba
3
我很高兴没有人试图与那个人交换CSV文件。你能想象所有数字数据被重新解释为字符串所带来的痛苦吗? - Donal Fellows
4
注意:String.Format使用Int32.ToString(),因此上述所有内容也适用于String.Format。 - Maris B.

31

在随机整数样本上测试,安装有Windows的所有352种文化(CultureTypes.InstalledWin32Cultures)都给出相同的结果。

丹尼尔正确地指出,自定义文化可以为负数使用不同的前缀,但我怀疑除了偶然情况外,没有人曾经使用过这个功能。

我想 .NET 开发人员之所以这样做是为了与 float 和其他类型保持一致。他们还期望什么呢?

> int.MaxValue.ToString(CultureInfo.AncientRome)
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM....

13
我感到了代码中的巨大扰动,仿佛数百万份家庭作业突然惊恐地哭泣着,然后猝然无声。 - Thomas Langston
你真的让我相信了,叹气。 - ErikE
4
实际上,古罗马人有一个更多才多艺的大数字表示系统 - http://www.knowtheromans.co.uk/img/Roman-Numerals-Large-Numbers.png;还需要更多jQuery! - user719662
1
+1 对于 CultureInfo.AncientRome。我们还需要 CultureInfo.AncientBabylon,但您需要安装楔形字体才能使用它。 - reirab
2
@vaxquis,该链接现已失效,请查看存档图片 - Bouke

6

很奇怪,我本来希望 50.ToString(CultureInfo.CreateSpecificCulture("ar-AE")) 的返回值是 "٥٠",但它并不是。

我刚查了一下,问题似乎是 NumberFormatInfo.DigitSubstitution 根本没有实现

DigitSubstitution 属性被保留以备将来使用。目前,它在当前 NumberFormatInfo 对象的解析或格式化操作中都未被使用。

因此,虽然有一个 System.Globalization.DigitShapes 枚举,但它实际上并没有在 IFormatProvider 的 NumberFormatInfo 部分中实现。


3
是的。它取决于当前的文化。根据MSDN文档

返回值使用通用数字格式说明符(“G”)和当前文化的NumberFormatInfo对象进行格式化。

重点强调

Resharper很可能希望您明确指定要使用的文化。因为省略它依赖于在不同计算机上执行时可能会更改的行为。


2
但是省略它将会使用线程中的当前文化,这很可能是您想要的(如果用户切换语言,则希望输出更改)。 - Magnus
4
@Magnus,如果您为显示格式化字符串,则只需使用当前文化。但如果您需要将其格式化以进行持久性存储(例如在XML/JSON/CSV等中),则最好使用不变的文化。请注意不要改变原始含义。 - Joe White
@JoeWhite XML/JSON序列化器无论如何都不会使用文化。但是,如果您想使用ToString()将Double保存为字符串,并将其转换回Double,则最好使用不变的文化。但我认为99%的情况下这不是您想要做的事情。 - Magnus

1
编译器(不必要地)警告我们,它可能被转换为字符串,但不是我们预期的方式。例如:
int i = 1;
Console.WriteLine("i.ToString='{0}'", i.ToString());

我们都期望它返回为“1”,但这并不是100%保证,因为.ToString()方法受当前线程文化的影响。它声称可能会返回“1.00”或类似的内容,但我用一个非常简短的代码进行了测试:
foreach(CultureInfo ci In System.Globalization.CultureInfo.GetCultures(CultureTypes.AllCultures) {
        Console.WriteLine("RESULT='{0}' CultureCode: {1} EnglishName:{2}", i.ToString(ci), ci.Name, ci.EnglishName);
        if (!i.ToString(ci).Equals("1"))
            throw new FormatException()
}

代码从未返回任何错误。因此,它实际上除了"1"以外什么也不返回。
只要在这个世界上有一个新的国家,使用一种全新的超级怪异的语言来表示整数"1.00",我们就可以毫无疑问地继续使用.ToString()。
干杯!
输出为:

1
听起来你运行了与Colonel Panic的答案中描述的相同的测试。 - Joe White

0

我本来会说不,但是在查看了MSDN Int32.ToString()之后,发现它是这样的:

返回值使用当前区域性的NumberFormatInfo对象与通用数字格式说明符("G")进行格式化。

所以这很惊讶。

问题应该是为什么当前的Resharper没有建议这样做?


-4

由于整数可以高达2,147,483,647。

某些国家,他们会使用小数或空格替换逗号。


5
默认格式不使用千位分隔符,因此这并不适用。 - Joe White

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接