.ToString() and .ToLower() gives warning about culture differences - c#

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

Related

Why do bool.TrueString and bool.FalseString exist?

I was reading the MSDN article of the boolean structure, when I saw that a boolean has two fields: TrueString and FalseString. These respectively return "True" and "False".
After some searching, the only example I could find is in this dotnetperls article. The article states:
Programs often need these strings. TrueString and FalseString are a useful pair of readonly members. They represent truth values in string format. They provide indirection and abstraction over directly using string literals.
So appearantly it's useful for some situations. But the same article fails to provide a realistic example (IMHO anyway).
Some further reading also brought this to my attention: TrueString and FalseString are public static readonly fields. And this dornetperls article states:
The language specification recommends using public static readonly fields ... when the field is subject to change in the future.
Now this I can somewhat understand. If the .NET developers ever decide to change "True" and "False" to respectively "OkeyDokey" and "Negatory", it's smart to use TrueString and or FalseString.
But that still leaves me with the question: in what kind of scenario do you want to compare a string with the string literal of a boolean? Because appearantly: "Programs often need" them.
For the same reason that string.Empty exists. Some people prefer a semantically named value in code over a literal one.
In modern .NET (anything after .NET Framework) the following code prints True three times:
Console.WriteLine(ReferenceEquals("True", bool.TrueString));
Console.WriteLine(ReferenceEquals("False", bool.FalseString));
Console.WriteLine(ReferenceEquals("", string.Empty));
This tells us there is zero runtime difference between the literals and the fields. They are exactly the same object at runtime.
Try this for yourself on sharplab.io here.
Others have mentioned using it to compare with when parsing boolean strings, but I would not recommend that. If you want to convert a string to a bool, use bool.TryParse or bool.Parse. Using == does a case-sensitive comparison, which is probably not what you want. Furthermore, the framework's methods are optimised specifically for common cases. You can see these optimisations in the code on GitHub here: https://github.com/dotnet/runtime/blob/f8fa9f6d1554e8db291187dd7b2847162703381e/src/libraries/System.Private.CoreLib/src/System/Boolean.cs#L226
If the program stores data in a human readable file or database, it may need to store values as strings. When you read the data back in, if you know the data was written by your application and uses a standard string representation, you can compare x == bool.TrueString faster than you can bool.TryParse(x ...). You could also validate the data by making sure all values x == bool.TrueString || x == bool.FalseString
If the data was typed by humans, or a different system, TryParse is a better option, as it accepts more values as true and differentiates between a definite false and an invalid input. (MSDN Boolean TryParse)
In easy words. Boolean is a Structure. this boolean expose ToString() method which represent a human readable text for the users. So if you write some thing like.
bool b = false;
b.ToString();
the output will be the "False" insteed of 0. the "False" is readable by human and easyly being captured.
Also some where you may want to parse a text value to a boolean value. so these also can be represented as boolean values. for example. we use
Boolean.TryParse("false" ,out mybool)
the false value is being set by the Tryparse method as this finds that we can read values from strings tool.
It can be used as a default value for missing "stringly-typed" configuration parameters. Here's a concrete example I've recently used:
if (bool.Parse(ConfigurationManager.AppSettings["IsTestMode"] ?? bool.FalseString)) ...
...which is - in my humble opinion - simpler and more readable than
var isTestModeString = ConfigurationManager.AppSettings["IsTestMode"];
if (isTestModeString != null && bool.Parse(isTestModeString)) ...
(I deliberately do not use TryParse here, since I do not want to silently ignore invalid values. I want an exception to be thrown, if the configuration value is present and something other than True or False.)
There are many situations where you may need to compare if a string is equal to "True", such as checking an API response. Note: it's more efficient to compare strings but often safer to parse.
The only advantage to using the built-in properties is you won't make typos (assuming you have Intellisense) and you don't have to remember the casing (e.g. "true" instead of "True).

Proper way to test if a value is numeric in C#

I just need to know if the value is numeric. I don't need to do anything with the value. Is this the best way? Feel dirty creating a variable that I won't ever use beyond this:
int val;
if(int.TryParse(txtFoo.Text, out val))
{
....
}
Yes, using the relevant TryParse method and ignoring the out parameter is the best way of doing this.
You may want to wrap this up into your own set of helper methods (which could specify the appropriate culture etc, if the default isn't right for you) and just return a bool without the out parameter to make them easier to call.
Of course, you need to work out what kind of parsing is most appropriate - even for integers, you need to consider whether the range of Int32 is enough for your use case. In my experience, most numeric input has its own "natural" range of valid values, which is unlikely to be exactly the range of any predefined type. You may therefore want to expand your helper methods to include the range of valid values to accept.
"is numeric" is an ambiguous term.
Culture-aware?
Allow thousands and/or decimal separators?
Allow scientific notation?
Allow a sign (before? after?...)
What range of values do you allow? Signed 32-bit integer (Int32.TryParse), Unsigned 32-bit integer (UInt32.TryParse), decimal, double, ...
Hence there is no "best" way, and the Framework provides a multitude of different ways to parse numbers.
You can use Regular expressions
Regex _isNumber = new Regex(#"^\d+$");
_isNumber.IsMatch(txtFoo.Text);
This will only match Ints, but you can write one that also matches decimals.
It's not as flexible as int.TryParse, but you could check to see if each character is a number:
bool isInt = txtFoo.Text.All(c => char.IsNumber(c));
In general, though, I would recommend sticking with int.TryParse. You can even call the unused parameter "ignored" to be explicit about your intent, e.g.:
int ignored;
bool isInt = int.TryParse(txtFoo.Text, out ignored);
That is the recommended way of doing it in C#. However, you could also add Microsoft.VisualBasic.dll as a reference to your project and then use Microsoft.VisualBasic.Information.IsNumeric()
You can try using Regex parsing to determine that there are no non-numeric characters in a string, or you can use Int.TryParse(), Double.TryParse(), Float.TryParse() depending on the input.
bool test (string teststring)
{ for (i=0;i==teststring.length;i++){
if instr("0123456789.,-+Ee",teststring.substring(i,1) <0){return false;}
// some additional tests below here if you like
return true;
}
however E1001E12e.12e would be noted as a number a little bit more magic is needed to do a clean check, but then you might be able to determine if its a int or a float too..
That's the best way of doing it in my knowledge - that's what our company standards adhere to anyway due to the error handling being done within the parsing.
This details the advantages: https://web.archive.org/web/20150510214425/http://www.dotnetperls.com:80/int-tryparse

How to prohibit comma as decimal separator

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.

String.Format vs ToString and using InvariantCulture

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.

C# Why won't this substring work? Error: Input string was not in a correct format

The problem is with the convert of the txt box value, but why?
string strChar = strTest.Substring(0, Convert.ToInt16(txtBoxValue.Text));
Error is: Input string was not in a correct format.
Thanks all.
txtBoxValue.Text probably does not contain a valid int16.
A good way to avoid that error is to use .tryParse (.net 2.0 and up)
int subLength;
if(!int.TryParse(txtBoxValue.Text,out subLength)
subLength= 0;
string strChar = strTest.Substring(0, subLength);
This way, if txtBoxValue.Textdoes not contain a valid number then subLength will be set to 0;
One thing you may want to try is using TryParse
Int16 myInt16;
if(Int16.TryParse(myString, out myInt16)
{
string strChar = strTest.Substring(0, myInt16);
}
else
{
MessageBox.Show("Hey this isn't an Int16!");
}
A couple reasons the code could be faulty.
To really nail it down, put your short conversion on a new line, like this:
short val = Convert.ToInt16(txtBoxValue.Text);
string strChar = strTest.Substring(0, val);
Likely the value in txtBoxValue.Text is not a short (it might be too big, or have alpha characters in it). If it is valid and val gets assigned, then strTest might not have enough characters in it for substring to work, although this normally returns a different error. Also, the second parameter of substring might require an int (not sure, can't test right now) so you may need to actually convert to int32 instead of 16.
What is the value of txtBoxValue.Text during your tests?
ASP.NET offers several validation controls for checking user input. You should use something like a CompareValidator or RegularExpressionValiditor in your WebForm if you're expecting a specific type of input, eg, an Integer.

Categories

Resources