74

I'm running a beta version of ReSharper, and it's giving me warnings for the following code:

int id;
// ...
DoSomethingWith(id.ToString());

The warning is on the id.ToString() call, and it's telling me "Specify a culture in string conversion explicitly". I understand the warning, and I know how to fix it -- just change the code to the much more unwieldy id.ToString(CultureInfo.InvariantCulture).

But my question is: is that necessary? I mean, obviously it's important to specify the culture when you're using types like DateTime (different cultures have different date formats) and Double (different characters used for the decimal point). But Int32.ToString(), at least in the en-US and invariant cultures, doesn't add any formatting at all. No commas, no decimal points, no dollar signs, nothing. So what would there be to vary by culture?

Are there some cultures that actually add some sort of formatting when you call the parameterless Int32.ToString()? Or is this a bug in the ReSharper beta, and this warning really isn't applicable to Int32 (in which case I'll file a ReSharper bug report)?

Kiley Naro
  • 1,749
  • 1
  • 14
  • 20
Joe White
  • 94,807
  • 60
  • 220
  • 330
  • Which ReSharper version are you using? As far as I know, there aren't any ReSharper versions in beta right now... – Kiley Naro Dec 13 '11 at 16:22
  • @KileyNaro: ReSharper 6.1 has been in EAP since early November. – Joe White Dec 13 '11 at 16:26
  • Also note that Visual Studio Code Analysis (aka FxCop) will complain about this, and nag you to explicitly set the culture. So it is not really Resharper's fault. – Polyfun Dec 13 '11 at 16:31
  • @ShellShock, sure; ReSharper just inspired the question, but the question is valid on its own, especially since the answer is surprising. – Joe White Dec 13 '11 at 16:41
  • 2
    6.1 is picking up lots of issues in our code where ToString() is called, but it doesn't seem to do the same for string.Format()? – Stephen Kennedy Jan 01 '12 at 17:17
  • .. if it is for diplay, logically you would change it to `id.ToString(CultureInfo.CurrentCulture)`. I recommend two **extension methods** be defined for all types you care about: `ToDisplayString()` and `ToInvariantString()`. Then no longer think about the type you have, just the purpose for which you are making the string. Self-documented code. – ToolmakerSteve Aug 27 '20 at 22:23

7 Answers7

76

The Operating System allows to change the negative sign for numbers.

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

So, current culture could have overridden the negative sign. In this case you need to respect the regional settings, this is the reason of the warning. You can also change the negative sign programatically:

    CultureInfo culture = Thread.CurrentThread.CurrentCulture;
    // Make a writable clone
    culture = (CultureInfo) culture.Clone();
    culture.NumberFormat.NegativeSign = "!";
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
31

As tested on a random sample of ints, all 352 cultures installed with Windows (CultureTypes.InstalledWin32Cultures) give identical results.

Daniel is right to note a custom culture could use a different prefix for negative numbers, but I doubt anyone has ever used this feature save by accident.

I guess the .NET devs did it to be consistent with float and other types. What else were they expecting?

> int.MaxValue.ToString(CultureInfo.AncientRome)
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM....
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 13
    I felt a great disturbance in the Code, as if millions of homework assignments suddenly cried out in terror, and were suddenly silenced. – Thomas Langston May 23 '14 at 18:19
  • You actually had me going, there. Sigh. – ErikE Dec 04 '15 at 23:38
  • 4
    +0 actually, ancient Romans had a more versatile system for large numbers - http://www.knowtheromans.co.uk/img/Roman-Numerals-Large-Numbers.png; also NOT ENOUGH jQuery!!! –  Jan 16 '16 at 18:11
  • 1
    +1 for `CultureInfo.AncientRome`. We need `CultureInfo.AncientBabylon`, too, but you'll need cuneiform fonts installed to use it. – reirab Jan 15 '19 at 17:39
  • 2
    @vaxquis the link is now defunct, see [the archived image](https://web.archive.org/web/*/http://www.knowtheromans.co.uk/img/Roman-Numerals-Large-Numbers.png). – Bouke Nov 11 '19 at 12:26
6

It's weird; I would have expected 50.ToString(CultureInfo.CreateSpecificCulture("ar-AE")) to return "٥٠", but it doesn't.

I've just looked this up, and the problem seems to be that NumberFormatInfo.DigitSubstitution isn't actually implemented

The DigitSubstitution property is reserved for future use. Currently, it is not used in either parsing or formatting operations for the current NumberFormatInfo object.

So, although there is an enumeration System.Globalization.DigitShapes, it's not actually implemented in the NumberFormatInfo bit of IFormatProvider.

Richard Gadsden
  • 839
  • 1
  • 6
  • 22
3

Yes. It depends on the current culture. From the MSDN docs:

The return value is formatted with the general numeric format specifier ("G") and the NumberFormatInfo object for the current culture.

emphasis mine

Resharper just most likely wants you to be explicit about what culture you are intending to use. Since omitting it relies on behavior that may change when executed on different machines.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • 2
    But omitting it will make it use the current culture in the thread, which is most likely what you want. (if the user switches language you _want_ the output to change) – Magnus Dec 13 '11 at 16:32
  • 4
    @Magnus, you only want the current culture if you're formatting a string for display. If you're formatting it for persistence (e.g. in XML/JSON/CSV/whatever), then you'd better use the invariant culture instead. – Joe White Dec 13 '11 at 16:37
  • @JoeWhite The xml/json sterilizer wont use the culture anyway. But yes, if you want to save a double as a string using `ToString()` somewhere and convert it back to a double you better use invariant culture. But I would say that 99% of the time that's not what you want to do. – Magnus Dec 13 '11 at 16:48
1

The compiler is (unnecessarily) warning us about it COULD be casted to string somehow not as we expected it to be. For example:

int i = 1;
Console.WriteLine("i.ToString='{0}'", i.ToString());

We all expect it to return as '1' but it is not 100% guaranteed because .ToString() method gets affected by the current thread culture. It CLAIMS that it could return as '1.00' or something like that but I tested it with a really small code:

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

The code never returned any error. So, it actually never returns anything other than "1".

As far as there will be a new country with a brand new native super-weirdo language in this world that actually spells "1.00" for an integer, we can keep using just .ToString() without any doubt.

Cheers!

Output was:

Roni Tovi
  • 828
  • 10
  • 21
0

I would have said no, but on checking MSDN Int32.ToString() there's this:

The return value is formatted with the general numeric format specifier ("G") and the NumberFormatInfo object for the current culture.

So there's a surprise.

The question should be why doesn't the current Resharper suggest this?

ChrisBD
  • 9,104
  • 3
  • 22
  • 35
-4

Because integers can be be as high as 2,147,483,647.

In some countries, they would use decimals or a space in place of the commas.

devlord
  • 4,054
  • 4
  • 37
  • 55