Is CultureInfo.CurrentCulture really necessary in String.Format()? - c#

How do you think is really necessary to provide IFormatProvider in method String.Format(string, object) ?
Is it better to write full variant
String.Format(CultureInfo.CurrentCulture, "String is {0}", str);
or just
String.Format("String is {0}", str);
?

In general, you will want to use InvariantCulture if the string you are generating is to be persisted in a way that is independent of the current user's culture (e.g. in the registry, or in a file).
You will want to use CurrentCulture for strings that are to be presented in the UI to the current user (forms, reports).
Subtle bugs can arise if you use CurrentCulture where you should be using InvariantCulture: bugs that only come to light when you have multiple users with different cultures accessing the same registry entry or file, or if a user changes his default culture.
Explicitly specifying CurrentCulture (the default if the IFormatProvider argument is omitted), is essentially documentation that demonstrates that you have considered the above and that the string being generated should use the current user's culture. That's why FxCop recommends that you should specify the IFormatProvider argument.

If you do not specify the IFormatProvider (or equivalently pass null) most argument types will eventually fall through to being formatted according to CultureInfo.CurrentCulture. Where it gets interesting is that you can specify a custom IFormatProvider that can get first crack at formatting the arguments, or override the formatting culture depending on other context.
Note that CultureInfo.CurrentCulture affects argument formatting, not resource selection; resource selection is controlled by CultureInfo.CurrentUICulture.

No, you do not need to specify the culture unless your string contains culture specific elements such as decimal separators, currency, etc., which have to be rendered depending on the culture.

It is especially useful if you care about localization (Globalization) in your application. That is, if you want your app to support multiple languages and culture specific formats, then you should use that.

Related

Is conversion from basic system types to string reversible?

When storing an object in a string, can I ensure that casting it back will perform successfully does it depend on something else?
DateTime dt = DateTime.UtcNow;
string pattern = dt.ToString();
DateTime retDt = DateTime.Parse(pattern);
This question is asking about the following object types:
TimeSpan
DateTime
int
long
float
double
I have tried reading several forums and saw no contradicting example. I also haven't
got any exceptions from my code yet, but I am sure I haven't tried all possible inputs/scenarios.
When using ToString you can specify a format (see this topic for more information) for numbers and time.
The format you would want to use in many these cases is the round-trip format ("R" for Single, Double, and BigInteger types, "O" for DateTime.) This format ensures all the information passes to the string, and can be parsed back to the exact same data.
Passing an IFormatProvider to ToString is also essential to avoid problems with locales (for example, different locales can use different characters for the decimal separator.) Using the static CultureInfo.InvariantCulture solves this problem.
Lastly, if you're trying to persist data and then retrieve it, you may want to consider using a serializer, which takes entire classes and writes them to various formats. .NET has several serializers, two of the most prominent ones being the BinaryFormatter (binary) and the DataContractSerializer (XML).
Parse and ToString being able to convert to and from the type of your choice depends on their implementation. For the default the conversion will work without any change.
Take care with DateTime though as you might want to specify to output the timezone information with ToString so that your parse will create the new DateTime with the correct time zone information.
You need to be aware that culture specific settings come into play when using the default ToString implementation for these types.
If you always deserialize on the same machine that did the serialization, it should not be an issue (unless the user can change their culture settings between serialization and deserialization).
If you intend to deserialize on a different machine you should try to use a culture invariant representation. All of these types have overloads of ToString that take a format specifier. Unfortunately, the culture invariant specifiers are all slightly different. For the numeric types it is "r" (the "round-trip" format). For DateTime, it is "o". For TimeSpan it is "c". Typically the Parse methods accept current culture format or invariant format. Watch out for ParseExact methods.

FxCop CA1305: CurrentCulture vs. CurrentUICulture

So, I have a few resource files for localization. They're strings that can be formatted.
For example:
MyResource.resx
Title: "MyApp - Ver: {0}"
I then return it by doing like so:
public String Title
{
get { return String.Format(CultureInfo.CurrentUICulture, MyResources.Title, 1); }
}
I understand the difference between CurrentUICulture and CurrentCulture, but FxCop is telling me to use CurrentCulture instead?
To some extent FxCop is right: you should not use CurrentUICulture in this case. As others already said, CurrentCulture is meant for Locale-aware formatting, whereas CurrentUICulture is meant for reading translatable strings from resources.
What you did here, was formatting number, therefore FxCop complains that you used incorrect CultureInfo. Unfortunately, what FxCop did not tell you is, you should in fact use CultureInfo.InvariantCulture. Why? Because version number is something that does not depend on Locale. You will always see something like 1.9 and not something like 1,9. Thus InvariantCulture is the way to go.
Microsoft even provided specific class to store version information - oddly enough its name is Version (AFAIR it is in System namespace). This will always present you version numbers like I mentioned before, when you do ToString(). Its constructor also expects Locale-invariant version string when you instantiate it.
String.Format is often going to be used for numeric or date formatting, which is based on CurrentCulture rather than CurrentUICulture.
Another thing is that CurrentUICulture can be a neutral culture while CurrentCulture is always a specific culture.
The XXXFormatInfo types do not work with neutral cultures and will raise a NotSupportedException exception.

CurrentCulture and SecurityException

We write some winForms GUI application that uses the Invariant Culture. So in the beginning of the Main we have:
[STAThread]
static void Main()
{
CultureInfo culture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
...
The problem is that on some machines it works perfectly but on some machines on some configurations ( like Debug/AnyCPU fro x64 machines) it raises SequrityException
Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
So what is the problem with it? And why it happens only on some conditions?
Another post mentions security issues when using the invariant culture. Perhaps this is your issue?
Using the InvariantCulture Property
The InvariantCulture property represents neither a neutral nor a
specific culture. It represents a third type of culture that is
culture-insensitive. It is associated with the English language but
not with a country or region. Your applications can use this property
with almost any method in the System.Globalization namespace that
requires a culture. However, an application should use the invariant
culture only for processes that require culture-independent results,
such as formatting and parsing data that is persisted to a file. In
other cases, it produces results that might be linguistically
incorrect or culturally inappropriate.
Security Considerations If a security decision will be made based
on the result of a string comparison or case change, your application
should use an ordinal comparison that ignores case instead of using
InvariantCulture. The default implementations of methods such as
Compare()()() and ToUpper use the CurrentCulture property. Code that
performs culture-sensitive string operations can cause security
vulnerabilities if CurrentCulture is changed or if the culture on the
computer running the code differs from the culture used to test the
code. The behavior that you expect when writing a string operation
differs from the actual behavior of your code on the executing
computer. In contrast, an ordinal comparison depends solely on the
binary value of the compared characters.
String Operations If your application needs to perform a
culture-sensitive string operation that is not affected by the value
of CurrentCulture, it should use a method that accepts a CultureInfo
parameter. The application should specify the value of the
InvariantCulture property for this parameter. The application should
use the property with methods such as Compare()()() and ToUpper to
eliminate cultural variations and ensure consistent results. For more
information about using the InvariantCulture property to perform
culture-insensitive string operations, see Culture-Insensitive String
Operations.
Persisting Data The InvariantCulture property is useful for
storing data that will not be displayed directly to users. Storing
data in a culture-independent format guarantees a known format that
does not change. When users from different cultures access the data,
it can be formatted appropriately based on specific user. For example,
if your application stores DateTime types in a text file, formatted
for the invariant culture, the application should use the
InvariantCulture property when calling ToString to store the strings
and the Parse method to retrieve the strings. This technique ensures
that the underlying values of the DateTime types do not change when
the data is read or written by users from different cultures.

Parse string to float number C#

I have float number in string. there is one problem. Number uses "." not "," as decimal point.
This code is not working:
MyNumber = float.Parse("123.5");
I know that I can use string replace function to "repair" this string before Parsing.
MyNumber = float.Parse("123.5".Replace('.',',');
But is there any other way to do it?
Using string replace is very fragile, and will lead to suttle bugs. Specify the IFormatProvider instead. For instance:
MyNumber = float.Parse("123.5", CultureInfo.InvariantCulture);
Or you can specify the NumberFormatInfo using another overload of Parse.
To add to Steven's question, rather than argue differently, the important thing is why the decimal separator is ..
If it's because the source is in a computer-readable format where the period decimal separator is specified as part of the document specification, then I'd go precisely as Steven does in using CultureInfo.InvariantCulture.
If it's human input in a particular locale, then you would want to match that locale by the CultureInfo appropriate for that locale, otherwise if the software is used with a different locale you'd have precisely the opposite problem. Generally you would want to set the thread's CurrentCulture to match this (CurrentCulture for formats, CurrentUICulture for languages). If you've done this, then you don't need to pass a culture at all, as the form float.Parse(string) uses that culture - however, you may wish to use float.Parse(string, CurrentCulture) to be explicit that this is what you are doing (and to shut up some software analysis that complains when you aren't specific in this way).
What gets really tricky, is if you potentially have to accept both period and comma - not least because many cultures that use period as a decimal separator, use comma as a thousands separator, and ultimately it's impossible to guarantee unambiguous parsing. However, assuming the thousands issue doesn't affect you, then the code you gave in your question is the approach, though I'd recommend doing the opposite (replace comma with period) and then parsing with the invariant culture, as that removes any further complications caused by yet more culture changes.
It depends on current culture of currently executed thread culture.
float.Parse("123,5", system.threading.thread.currentthread.currentculture);
float.Parse("123.5", system.threading.thread.currentthread.currentculture);
IF you strictly do not want culturespecific then
float.Parse("123.5", CultureInfo.InvariantCulture);

Why do I get a FormatException when converting a string to a float?

When I try to convert a string to float:
Console.WriteLine(float.Parse("6.59"));
it throws an exception:
Unhandled Exception: System.FormatException: Input string was not in a correct f
ormat.
at System.Number.ParseSingle(String value, NumberStyles options, NumberFormat
Info numfmt)
When I try it like this:
Console.WriteLine(Convert.ToSingle("6.59"));
It throws the same exception:
Unhandled Exception: System.FormatException: Input string was not in a correct f
ormat.
at System.Number.ParseSingle(String value, NumberStyles options, NumberFormat
Info numfmt)
at System.Convert.ToSingle(String value)
Can you explain why this happens?
The single argument Parse method uses the current culture to parse the string. If your current culture uses some other decimal separator, this will fail.
Try using the invariant culture:
float.Parse("6.59", CultureInfo.InvariantCulture)
The problem here is your culture.
Either set the invariant culture like this:
float.Parse("6.59", CultureInfo.InvariantCulture)
or use the correct decimal separator for your culture
float.Parse("6,59")
I wonder why you are using a literal string. If you are having problems entering literal floats, you can use
Console.WriteLine(6.59f)
If you do it this way culture doesn't matter because the value is decided at compile time.
You are probably using a culture that uses the , as a decimal seperator.
You could try to Parse using the InvariantCulture:
float.Parse("6.59", CultureInfo.InvariantCulture)
Culture - specific things. What's your default culture?
Some cultures use "," instead of ".". You can try this:
float.Parse("6.59", CultureInfo.InvariantCulture);
There could be problem with Locale/Culture. You need to set , instead of . for the decimal separator.
I know everyone here has already given the reason for the problem experienced but perhaps somebody should just expand on why Invariant fixes it.
The CultureInfo class is used either directly or indirectly by classes that format, parse, or manipulate culture-specific data, such as String, DateTime, DateTimeOffset, and the numeric types to deal with the differences in the way different cultures write these types.
In case of the decimal type some cultures use a period(.) whilst others use a comma (,). By default when you are using the Conversion Libraries it will make use of your local culture (that is the country your OS to configured for).
By specifying Invariant you say that you expect thousand separators to be commas(,) and decimal deliminator to be a period(.) as it is in most cultures.
A big problem that sometimes happens is that these cultural conventions change from the OS point of view. For example the South African (ZA) culture info used to behave like the invariant culture. Microsoft changed this with Windows 8 where the decimal suddenly became a comma and the thousand separator a space.This resulted in many legacy systems written in .Net suddently breaking when one migrated them to newer operating systems.
In the end, deal normalize all local culture info to invariant and persist and deal with them in your business logic in this format. Then localize it back on the front end. Same goes for DateTime, as soon as possible convert to UTC, and only back when you render an output.
You could also try Convert class to perform this task.
Convert.ToDecimal("6.59");

Categories

Resources