I am a little confused here.
What should I use
Console.WriteLine((val/1085).ToString("N"));
VS
Console.WriteLine(String.Format("{0:N}", (val/1085)));
Also how do I fit the InvariantCulture? ANY BEST PRACTICES :)?
Actually I prefer a third form:
Console.WriteLine("{0:N}", val / 1085);
Console.WriteLine can do the String.Format for you.
Console.WriteLine does not allow you to supply a culture. If that is what you want, you will still have to use String.Format. As in:
String.Format(CultureInfo.InvariantCulture, "{0:N}", 123456789);
I do not recommend using that because international users will have trouble reading that. To me 123,456,789.00 looks strange.
For formatting + culture I prefer:
.ToString("####0.00",CultureInfo.InvariantCulture)
or
.ToString("N",CultureInfo.InvariantCulture)
I found an invariant and generic way to solve that as follows:
Syntax:
.ToStringInvariant(format)
.ToStringInvariant()
Technically, it is a generic extension method, defined as follows:
public static class Extensions
{
private static IFormatProvider inv
= System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
public static string ToStringInvariant<T>(this T obj, string format=null)
{
return (format == null) ? System.FormattableString.Invariant($"{obj}")
: String.Format(inv, $"{{0:{format}}}", obj);
}
}
Usage is simple, just use .ToStringInvariant() instead of .ToString(). The advantage is that it works for any data type.
Optionally, you can pass a format too, like for example .ToStringInvariant("N"), just as you are used to to it with .ToString("N").
Note that in that case the extension method uses String.Format internally.
You can see the difference if you have a different culture for the number format, like in Germany we have comma instead of a decimal point. That means on a PC with German culture settings:
void Main()
{
var val = 2456.5;
Console.WriteLine((val/1085).ToString("N"));
Console.WriteLine((val/1085).ToStringInvariant("N"));
Console.WriteLine((val/1085).ToStringInvariant("0.000"));
Console.WriteLine((val/1085).ToStringInvariant());
}
it would output:
2,26
2.26
2.264
2.26405529953917
which is correct, because .ToString uses the current culture (German settings), and .ToStringInvariant always uses the invariant culture, which is the English number format regardless of the Windows settings.
Note: For date formatting, I have provided a different extension method, which you can find here.
More information:
FormattableString.Invariant(FormattableString) Method
In a datetime it's okay to use both. I rather like to use and see the first solution (ofcourse with missing parenthesis).
The String.Format is much more usefull when you have some string with a gaps for some kind of parameters. Then it's a killer method, which really nicely help you to organize your code.
Related
I am storing some boolean values as string session variables, like this:
HttpContext.Session.SetString("SomeBool", user.SomeBool.ToString().ToLower());
This gives me warnings that the result could vary depending on the users culture. How is that possible? In what way could the result of ToString() or ToLower() of "True" or "False" vary depending on culture? Aren't boolean values always represented by the English words "True" and "False", no matter the culture of the database or hosting environment?
I've also tried these three, which all gives exactly the same warning:
HttpContext.Session.SetString("SomeBool", FormattableString.Invariant($"{user.SomeBool.ToString().ToLower()}"));
HttpContext.Session.SetString("SomeBool", String.Format(CultureInfo.CurrentCulture, $"{0}", user.SomeBool.ToString().ToLower()));
HttpContext.Session.SetString("SomeBool", String.Format(CultureInfo.CurrentCulture, user.SomeBool.ToString().ToLower()));
VS suggests I can disable CA1305 warnings, but I don't want to do that.
Any suggestions?
Update
Although VillageTech's answer answers my question, I have changed my code to avoid the issue altogether. Inspired by Christopher's suggestion about hard coding the values:
HttpContext.Session.SetString("SomeBool", user.SomeBool ? "true" : "false");
Do not store stuff as string. Arguably string is the 2nd worst type for processing. The only one worse is raw binary.
In the rare case that you have to store something as String, you must make certain to pick the same Culture Settings and Encoding at all endpoints. By default the ToString() and Parse() and generally string related functions extract the current Culture and it's settings from Windows. This is very usefull for the normal GUI, but very bad for such a case. Those kinds of issues is what the warning is about. Hardcode one instead.
XML and JSON do take care of those things for you. But in this case you have to do it manually.
Both ToString() and ToLower() can emit such warnings.
Use this:
HttpContext.Session.SetString("SomeBool", user.SomeBool.ToString(CultureInfo.CurrentCulture).ToLower(CultureInfo.CurrentCulture));
Try this
// calling getValue() method
stringSomeBool = getValue(user.SomeBool);
HttpContext.Session.SetString("SomeBool", stringSomeBool.ToLower());
// defining getValue() method
public static void getValue(bool variable)
{
// getting the value of string property
string value = variable.ToString();
// print the string property
Console.WriteLine("{0}", value);
}
my first contribution:
Try defining it as a string before you use the variable like so:
string convd = user.SomeBool.ToString();
convd = convd.ToLower();
HttpContext.Session.SetString("SomeBool", convd);
This is what i normally would do,
hope this helps as my first answer :))
Let FormattableString.Invariant do the ToString() for you:
using static System.FormattableString;
HttpContext.Session.SetString("SomeBool", Invariant($"{user.SomeBool}").ToLower();
I have been recently performing code reviews on quite a large system which has users around the world.
I noticed that some developers are displaying dates using the following code:
userDOB.ToString("d")
and others are using this:
userDOB.ToShortDateString()
Is there any difference between what these 2 lines should output?
If not then i am going to push for having some consistency and using one of these formats, personally i prefer ToShortDateString() as it is more readable (unless someone has a better reason to use ToString("d")).
If you see both methods
public string ToShortDateString()
{
return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo);
}
and
public string ToString(string format)
{
return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo);
}
you can see the difference for your self that in ToString you have to define format in your case "d" and in ToShortDateString the format is predefined but they both are pointing to the same method DateTimeFormat.Format
Having created an own number type (actually DoubleDouble), I want to implement the IFormattable interface. So I have to somehow parse the format string.
public string ToString(string format, IFormatProvider formatProvider) {
// formatting string according to format and using formatprovider?
return formattedString;
}
The user of the class should be able to use it as a replacement for double (or any other number format).
String.Format("{0:0.##}", (DoubleDouble)123.4567);
My question is, does someone know a good tutorial about this or can give me some hints?
How to support localizing in this process?
How to parse the format string? Are there some methods to aid in this task or do I have to do it all by "hand" with regexp and such?
I really searched for help but couldn't find any, if you find something in another language (C,C++) that may help, please tell me about it.
MSDN has a nice example of a Temperature class that implements the IFormattable interface with its own custom format.
I think you know this already; anyway, today I learned that if your DoubleDouble class implemented the IFormattable interface then:
String.Format("{0:0.##}", (DoubleDouble)123.4567);
... would call the DoubleDouble class's ToString(...) implementation with the specific format "0.##" as the first parameter, which I suspect is close to what you want. You still have to parse that part of the format though.
I would hazard a guess that much of the format parsing is embedded deep in the highly optimised .Net library binaries, so you don't get any custom parsing virtual methods to help.
Maybe this can help you:
var my = DoFormat(123.0)
public static string DoFormat( double myNumber )
{
var s = string.Format("{0:0.00}", myNumber);
if ( s.EndsWith("00") )
{
return ((int)myNumber).ToString();
}
else
{
return s;
}
}
How to support localizing in this process?
Something like the following:
public string ToString(string format, IFormatProvider formatProvider)
{
CultureInfo culture = formatProvider as CultureInfo;
if (culture != null)
{
// Now you can do things like
// culture.NumberFormat.NumberDecimalSeparator, etc.
}
}
How to parse the format string? Are there some methods to aid in this
task or do I have to do it all by "hand" with regexp and such?
There's no public API in the .NET framework to do it for you. If you look at the source for the .NET primitive numeric types' implementation of IFormattable, they all eventually call external methods, so they provide no insight. You'll probably need to use something like RegEx's to parse the format string. Then you can divide by the largest power of 10 to figure out what the first digit of output will be, subtract that out, then repeat for the lesser power's of 10 until you have generated all the digits.
I had written a JavaScript number formatting function a few years ago that mimicked most of the .NET way of parsing format strings. It may be of some help for you in writing a C# version of it. At the very least it can give you a starting point.
https://github.com/flamewave/jquery-ui-numeric/blob/master/jquery-ui-numeric.js
Scroll all the way down to the bottom to the $.formatNumber function.
Note: I no longer maintain this function in favor of using the Globalize library, which also may give you some insight into parsing format strings.
I'm writing code with german culture settings
Nevertheless I would like to force the user to use the point as a decimal separator.
My piece of test code outputs the wrong value.
How do I detect the "wrong" comma ?(throw an exception)
string nok_str = "14,9";
string ok_str = "14.9";
double nok_Var1 = double.Parse(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
double nok_Var2 =Convert.ToDouble(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
First off, and please forgive me, Iād like to question your design decision:
How is this enhancing the user experience? The application should rather try to accept all unambiguous user input, not reject theoretically sound input.
That said, a number such as ā19,2ā will be interpreted, with an invariant culture, as having a thousands separator (which is simply discarded). This is why your code silently produces bad values. If you want to explicitly forbid this input, the easiest way to achieve this is an explicit test:
if (nok_str.Contains(","))
throw new FormatException(ā¦);
As an alternative, you can try modifying the NumberFormatInfo.NumberGroupSeparator property of a custom NumberFormatInfo object that you pass to the Parse method.
Basically the default is to include AllowThousands in the number style. If you specify the number style you want, you can prohibit this:
using System;
using System.Globalization;
class Test
{
static void Main(string[] args)
{
string text = "19,2";
double value;
bool valid = double.TryParse(text, NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
Console.WriteLine(valid); // Prints false
}
}
Note that NumberStyles.Float is a composite style for AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowDecimalPoint, and AllowExponent - but not AllowThousands.
I am not sure what is the source of your input.
If it comes from user it also depends... If it is GUI application, you may think of restricting the input to certain possible keys, excluding comma. If it is a console app, you can try regular expressions to pre-validate input strings.
If it comes from various sources (i.e. web service) maybe simply brute-force string replace will do the trick?
Last, but not least: there are reasons for parsing to be culture-sensitive and if I were you, I would encourage users to enter valid regional number format instead forcing them to provide incorrect one.
When doing a string comparison in C#, what is the difference between doing a
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.CurrentCultureIgnoreCase);
and
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.InvariantCultureIgnoreCase);
... and is it important to include that extra parameter, anyway?
The other posts have given good advice, but I thought it might be nice to show an example of where it definitely makes a difference:
using System;
using System.Globalization;
using System.Threading;
class Test
{
static void Main()
{
CultureInfo turkish = CultureInfo.CreateSpecificCulture("tr");
Thread.CurrentThread.CurrentCulture = turkish;
// In Turkey, "i" does odd things
string lower = "i";
string upper = "I";
// Prints False
Console.WriteLine(lower.Equals(upper,
StringComparison.CurrentCultureIgnoreCase));
// Prints True
Console.WriteLine(lower.Equals(upper,
StringComparison.InvariantCultureIgnoreCase));
}
}
(There are no doubt many other cases - this was just the first one I thought of.)
Microsoft gives some decent guidance for when to use the InvariantCulture property:
MSDN: CultureInfo.InvariantCulture Property
... 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. [...]
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.
[...]
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. [...]