Use null-conditional operator to set value to 0 if null - c#

I'm new to C# but not to programming in general.
I am trying to set add some error checking to my program. There are 3 textboxes and I am trying to make it so that if the text box is left blank, it assumes a value of 0. Here is my code so far:
private void btnCalculate_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(txtNumberOfClassATix.Text)) // Assumes 0 if no number entered for txtNumberOfClassATix.Text.
{
txtNumberOfClassATix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassBTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassBTix.Text.
{
txtNumberOfClassBTix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassCTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassCTix.Text.
{
txtNumberOfClassCTix.Text = "0";
}
int classANum = int.Parse(txtNumberOfClassATix.Text);
int classBNum = int.Parse(txtNumberOfClassBTix.Text);
int classCNum = int.Parse(txtNumberOfClassCTix.Text);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}
This code works but I'm sure I could replace those if statements with something simpler. I've seen how to set a variable to null if another is null using the null-conditional operator but I don't really grasp it enough to adapt it to my scenario.

So far maccettura's answer is the best, but can we do better? Sure we can. Let's make a general-purpose extension method:
internal static class Extensions
{
public static int? AsInt(this string s)
{
int result;
if (s == null)
return null;
else if (int.TryParse(s, out result))
return result;
else
return null;
}
}
And now:
int classANum = txtNumberOfClassATix.Text.AsInt() ?? 0;
If it's an int, you get the int. If it's not, you get zero. Easy peasy.
Or, you might want this extension method:
internal static class Extensions
{
public static int AsInt(this string s, int default = 0)
{
int result;
if (s == null)
return default;
else if (int.TryParse(s, out result))
return result;
else
return default;
}
}
And now you can say what you want the default to be without using ??.
This style of programming is called "fluent programming"; it can make code that is very easy to read and understand.
Notice that this solution does not update the UI with zeros; if you wanted to do that then I would recommend splitting that into two steps: one which causes the mutation, and then a separate step which computes the value. Operations which are useful for both their effects and their values can be confusing.

This is a perfect time to use a method so you arent repeating yourself:
private static int GetInputAsInt(TextBox textbox)
{
int outputValue = 0;
if(textbox?.Text != null && int.TryParse(textbox.Text, out outputValue))
{
return outputValue;
}
return 0;
}
Now you are checking if the textbox itself is not null, and that the value contained therein is a int, if anything fails it returns a 0;
Call it in your other method like this:
int classANum = GetInputAsInt(txtNumberOfClassATix);
Which means your button click event would be a bit simpler:
private void btnCalculate_Click(object sender, EventArgs e)
{
int classANum = GetInputAsInt(txtNumberOfClassATix);
int classBNum = GetInputAsInt(txtNumberOfClassBTix);
int classCNum = GetInputAsInt(txtNumberOfClassCTix);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}

To keep it simple, a good approach is to use the conditional operator. The full example is below (broken across two lines for readability):
txtNumberOfClassATix.Text =
String.IsNullOrEmpty(txtNumberOfClassATix.Text) ? "0" : txtNumberOfClassATix.Text;
This is a nice, readable, assignment for the first part:
myString = ...
The conditional operator breaks down by providing a boolean expression (true/ false) on the left side of the ?. So, for example:
myString = anotherString == "" ? ... // checking if another string is empty
The final part is the :. To the left is the assignment if the expression is true, and to the right goes the assignment if the expression is false. To finish the example:
myString = anotherString == "" ? "anotherString is empty" : "anotherString is not empty";
The above example can be written out in full to clear up any misunderstanding as:
if (anotherString == "")
{
myString = "anotherString is empty";
}
else
{
myString = "anotherString is not empty";
}
This can apply to all the statements. The documentation is found here.

The best way to reduce the line of code is use the function for your common operation(s). In your case, you can create function which checks whether or not the object is NULL or empty. Based on the return value of that function you can proceed ahead. On the other hand, you can handle it on front-end by using different validators such as RequiredFieldValidator, CustomValidator, etc.

Related

Replace if else statement with a single method containing a conditiion

I am still learning C# but I've been on annual leave and I have come back to work and seen this piece of code my senior has left me before he went on his annual leave:
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
if (basketLocation.ToLower() == "top")
return basketTotalPrice[0].Text.Replace("£", "");
else
return basketTotalPrice[1].Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
return basketLocation == "top" ? 0 : 1;
}
I am assuming instead of using the if else statement, he wants me to use his new method of GetElementIndexForBasketLocation.
My question is simply how to implement this change?
Thanks
It's not totally clear what you're looking for, but you can rework the code something like this:
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
int index = GetElementIndexForBasketLocation(basketLocation);
return basketTotalPrice[index].Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
return basketLocation.ToLower() == "top" ? 0 : 1;
}
It looks like the method he provided you isn't calling ToLower(), leaving it to the client to do that work, which could lead to mistakes down the road.
Also, you might consider using string.Equals with StringComparison.OrdinalIgnoreCase instead of ToLower.
And it might be a good idea to add a null check before trying to call a method on the string, so you can throw an ArgumentNullException instead of the NullReferenceException that the current code will throw.
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
return basketTotalPrice[GetElementIndexForBasketLocation(basketLocation)]
.Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
if (basketLocation == null) throw new ArgumentNullException(nameof(basketLocation));
return basketLocation.Equals("top", StringComparison.OrdinalIgnoreCase) ? 0 : 1;
}

Whoa, what the TryParse

I've got a Session that contains particular integer values, which are indexed with given controls. Normally, the following would work just fine:
int value;
int.TryParse(Session["Key"].ToString(), out value);
However, I do need to account for null. Where, if the string fails the default out would return a null. Except I noticed that int.TryParse doesn't work with:
int? value = null;
int.TryParse(Session["Key"].ToString(), out value);
So how can you try that parse, if fails it results in the null?
I found this question and the Microsoft Developer Network dictates:
When this method returns, contains the signed integer value
equivalent of the number contained in s, if the conversion succeeded,
or zero if the conversion failed. The conversion fails if the string
parameter is null or String.Empty, is not of the correct format, or
represents a number less than Min Value or greater than Max Value. This
parameter is passed uninitialized.
Which plainly states, if int.TryParse fails the integer will hold a value of zero. In the instance of my usage, zero could be a valid value. So I need null, any thoughts?
Sure; utilize the return value of int.TryParse (which returns if the conversion succeeded or not):
int? retValue = null;
int parsedValue = 0;
if (int.TryParse(Session["Key"].ToString(), out parsedValue))
retValue = parsedValue;
else
retValue = null;
return retValue;
A little verbose I'll admit, but you could wrap it in a function.
int tmp;
int? value = int.TryParse(Session["Key"].ToString(), out tmp) ? (int?)tmp : null;
The problem is the word "null." What does it mean? null could mean the value was indeterminable, an exception was thrown, simply that the value is null, or some other contextual meaning. Your question is a perfect example, because you, yourself, are arbitrarily stating that, in your opinion, null means the parsing of the string failed.
Microsoft's TryParse paradigm is great, but for limited usage. Consider these Scenarios:
string == "89"
string == null
string == "Hello World"
string == ""
string == "2147483650"
Yet, your only options are to assign an Integer or Null to your output, and to return true or false.
Assuming it worked, what are you going to do with that information? Something like this?
int? value = null;
if (int.TryParse(Session["Key"].ToString(), out value)) {
if (value == null)
// Handle "Appropriate" null
else
// Handle appropriate numeric value
}
else {
// Note: value == null here, and TryParse failed
// Handle null...
// What if the reason it failed was because the number was too big?
// What if the string was Empty and you wanted to do something special?
// What if the string was actually junk? Like "(423)322-9876" ?
// Long-Story Short: You don't know what to do here without more info.
}
Consider this NullableInt TryParse example:
public bool TryParseNullableInt(string input, out int? output)
{
int tempOutput;
output = null;
if (input == null) return true;
if (input == string.Empty) return true; // Would you rather this be 0?
if (!int.TryParse(input, out tempOutput))
return false; // What if string was "2147483650"... or "Twenty Three"?
output = tempOutput;
return true;
}
One solution is to use an enumeration TryParse instead of a boolean TryParse:
public ParseStatus TryParseNullableInt(string input, out int? output)
{
int tempInteger;
output = null;
if (input == null) return ParseStatus.Success;
if (input == string.Empty) { output = 0; return ParseStatus.Derived; }
if (!int.TryParse(input, out tempInteger)) {
if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
output = tempInteger;
return ParseStatus.Derived;
}
long tempLong;
if (long.TryParse(input, out tempLong))
return ParseStatus.OutOfRange;
return ParseStatus.NotParsable;
}
output = tempInteger;
return ParseStatus.Success;
}
Another problem is the existence of the out variable. Your third option is to use a descriptive monad, something like this:
public Maybe<int?> TryParseNullableInt(string input)
{
if (input == null) return Maybe.Success(null);
if (input == string.Empty) { return Maybe.Derived(0); }
int tempInteger;
if (!int.TryParse(input, out tempInteger)) {
if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
return Maybe.Derived(tempInteger);
}
long tempLong;
if (long.TryParse(input, out tempLong))
return Maybe.OutOfRange();
return Maybe.NotParsable();
}
return Maybe.Success(tempInteger);
}
You can use Monads as Single-Enumerable Values, or like so:
Maybe<int?> result = TryParseNullableInt("Hello");
if (result.HasValue) {
if (result.Status == ParseStatus.Success)
// Do something you want...
else if (result.Status == ParseStatus.Derived)
// Do something else... more carefully maybe?
}
else if (result.Status == ParseStatus.OutOfRange)
MessageUser("That number is too big or too small");
else if (result.Status == ParseStatus.NotParsable)
// Do something
With Monads, and possibly enumeration TryParses, you now have all the info you need from a descriptive return and nobody has to guess what null might mean.

Differentiating between cases where user enters '0' or leaves TextBox empty

Well the question title may not be self explanatory, so let me go ahead and elaborate.
Consider, a TextBox that accepts only numeric value or is left empty. The value(text) entered is stored in an integer(int32) variable. The problem arises when the user enters the digit 0 or leaves the TextBox empty, as the conversion from string to int, converts an empty string to "0" as well.
So my question stands: How do I differentiate the 2 scenarios?
EDIT I figured a lot of questions may be answered by the code and exact problem(as I see it)
if (txtOtherId.Text == string.Empty)
{
otherId = Convert.ToInt32(null);
}
else
{
otherId = Convert.ToInt32(txtOtherId.Text);
}
How about an extension method?
public static class Extensions
{
public static bool TryGetInt(this TextBox tb, out int value)
{
int i;
bool parsed = int.TryParse(tb.Text, out i);
value = i;
return parsed;
}
}
Usage:
int i;
if (textBox1.TryGetInt(out i))
{
MessageBox.Show(i.ToString());
}
else
{
// no integer entered
}
What have you tried? Can we see your code?
Now, I tried the following:
int i;
i = Convert.ToInt32(""); // throws, doesn't give zero
i = int.Parse(""); // throws, doesn't give zero
bool couldParse = int.TryParse("", out i); // makes i=0 but signals that the parse failed
So I can't reproduce. However, if I use null instead of "", the Convert.ToInt32 does convert into zero (0). However, Parse and TryParse still fail with null.
UPDATE:
Now that I see your code. Consider changing the type of otherId from int to int? where the question mark makes it a nullable type. Then:
if (txtOtherId.Text == "")
{
otherId = null; // that's null of type int?
}
else
{
otherId = Convert.ToInt32(txtOtherId.Text); // will throw if Text is (empty again or) invalid
}
If you want to be sure no exceptions can happen, do this:
int tmp; // temporary variable
if (int.TryParse(txtOtherId.Text, out tmp))
otherId = tmp;
else
otherId = null; // that's null of type int?; happens for all invalid input
You could use a nullable int, and then have blank string be null.
int? myValue = String.IsNullOrEmpty(myTextbox.Text)
? (int?)null
: int.Parse(myTextbox.Text);
For clarity, the above is equivalent to
int? myValue = null;
if(!String.IsNullOrEmpty(myTextbox.Text))
{
myValue = int.Parse(myTextbox.Text);
}
Assuming that it is indeed a textbox...
string result = myTextBox.Text;
if (string.IsNullOrEmpty(result))
// This is an empty textbox
else
// It has a number in it.
int i = int.Parse(result);
There're 2 simple approaches how to do it:
string inputText="";
int? i=null;
if (!string.IsNullOrWhiteSpace(inputText))
i = int.Parse(inputText);
int i2;
bool ok = int.TryParse(inputText, out i2);

Best solution for an StringToInt function in C#

I were asked to do an StringToInt / Int.parse function on the white board in an job interview last week and did not perform very good but I came up with some sort of solution. Later when back home I made one in Visual Studion and I wonder if there are any better solution than mine below.
Have not bothred with any more error handling except checking that the string only contains digits.
private int StrToInt(string tmpString)
{
int tmpResult = 0;
System.Text.Encoding ascii = System.Text.Encoding.ASCII;
byte[] tmpByte = ascii.GetBytes(tmpString);
for (int i = 0; i <= tmpString.Length-1; i++)
{
// Check whatever the Character is an valid digit
if (tmpByte[i] > 47 && tmpByte[i] <= 58)
// Here I'm using the lenght-1 of the string to set the power and multiply this to the value
tmpResult += (tmpByte[i] - 48) * ((int)Math.Pow(10, (tmpString.Length-i)-1));
else
throw new Exception("Non valid character in string");
}
return tmpResult;
}
I'll take a contrarian approach.
public int? ToInt(this string mightBeInt)
{
int convertedInt;
if (int.TryParse(mightBeInt, out convertedInt))
{
return convertedInt;
}
return null;
}
After being told that this wasn't the point of the question, I'd argue that the question tests C coding skills, not C#. I'd further argue that treating strings as arrays of characters is a very bad habit in .NET, because strings are unicode, and in any application that might be globalized, making any assumption at all about character representations will get you in trouble, sooner or later. Further, the framework already provides a conversion method, and it will be more efficient and reliable than anything a developer would toss off in such a hurry. It's always a bad idea to re-invent framework functionality.
Then I would point out that by writing an extension method, I've created a very useful extension to the string class, something that I would actually use in production code.
If that argument loses me the job, I probably wouldn't want to work there anyway.
EDIT: As a couple of people have pointed out, I missed the "out" keyword in TryParse. Fixed.
Converting to a byte array is unnecessary, because a string is already an array of chars. Also, magic numbers such as 48 should be avoided in favor of readable constants such as '0'. Here's how I'd do it:
int result = 0;
for (int i = str.Length - 1, factor = 1; i >= 0; i--, factor *= 10)
result += (str[i] - '0') * factor;
For each character (starting from the end), add its numeric value times the correct power of 10 to the result. The power of 10 is calculated by multiplying it with 10 repeatedly, instead of unnecessarily using Math.Pow.
I think your solution is reasonably ok, but instead of doing math.pow, I would do:
tmpResult = 10 * tmpResult + (tmpByte[i] - 48);
Also, check the length against the length of tmpByte rather than tmpString. Not that it normally should matter, but it is rather odd to loop over one array while checking the length of another.
And, you could replace the for loop with a foreach statement.
If you want a simple non-framework using implementation, how 'bout this:
"1234".Aggregate(0, (s,c)=> c-'0'+10*s)
...and a note that you'd better be sure that the string consists solely of decimal digits before using this method.
Alternately, use an int? as the aggregate value to deal with error handling:
"12x34".Aggregate((int?)0, (s,c)=> c>='0'&&c<='9' ? c-'0'+10*s : null)
...this time with the note that empty strings evaluate to 0, which may not be most appropriate behavior - and no range checking or negative numbers are supported; both of which aren't hard to add but require unpretty looking wordy code :-).
Obviously, in practice you'd just use the built-in parsing methods. I actually use the following extension method and a bunch of nearly identical siblings in real projects:
public static int? ParseAsInt32(this string s, NumberStyles style, IFormatProvider provider) {
int val;
if (int.TryParse(s, style, provider, out val)) return val;
else return null;
}
Though this could be expressed slightly shorter using the ternary ? : operator doing so would mean relying on side-effects within an expression, which isn't a boon to readability in my experience.
Just because i like Linq:
string t = "1234";
var result = t.Select((c, i) => (c - '0') * Math.Pow(10, t.Length - i - 1)).Sum();
I agree with Cyclon Cat, they probably want someone who will utilize existing functionality.
But I would write the method a little bit different.
public int? ToInt(this string mightBeInt)
{
int number = 0;
if (Int32.TryParse(mightBeInt, out number))
return number;
return null;
}
Int32.TryParse does not allow properties to be given as out parameter.
I was asked this question over 9000 times on interviews :) This version is capable of handling negative numbers and handles other conditions very well:
public static int ToInt(string s)
{
bool isNegative = false, gotAnyDigit = false;
int result = 0;
foreach (var ch in s ?? "")
{
if(ch == '-' && !(gotAnyDigit || isNegative))
{
isNegative = true;
}
else if(char.IsDigit(ch))
{
result = result*10 + (ch - '0');
gotAnyDigit = true;
}
else
{
throw new ArgumentException("Not a number");
}
}
if (!gotAnyDigit)
throw new ArgumentException("Not a number");
return isNegative ? -result : result;
}
and a couple of lazy tests:
[TestFixture]
public class Tests
{
[Test]
public void CommonCases()
{
foreach (var sample in new[]
{
new {e = 123, s = "123"},
new {e = 110, s = "000110"},
new {e = -011000, s = "-011000"},
new {e = 0, s = "0"},
new {e = 1, s = "1"},
new {e = -2, s = "-2"},
new {e = -12223, s = "-12223"},
new {e = int.MaxValue, s = int.MaxValue.ToString()},
new {e = int.MinValue, s = int.MinValue.ToString()}
})
{
Assert.AreEqual(sample.e, Impl.ToInt(sample.s));
}
}
[Test]
public void BadCases()
{
var samples = new[] { "1231a", null, "", "a", "-a", "-", "12-23", "--1" };
var errCount = 0;
foreach (var sample in samples)
{
try
{
Impl.ToInt(sample);
}
catch(ArgumentException)
{
errCount++;
}
}
Assert.AreEqual(samples.Length, errCount);
}
}

What is the C# equivalent of NaN or IsNumeric?

What is the most efficient way of testing an input string whether it contains a numeric value (or conversely Not A Number)? I guess I can use Double.Parse or a regex (see below) but I was wondering if there is some built in way to do this, such as javascript's NaN() or IsNumeric() (was that VB, I can't remember?).
public static bool IsNumeric(this string value)
{
return Regex.IsMatch(value, "^\\d+$");
}
This doesn't have the regex overhead
double myNum = 0;
String testVar = "Not A Number";
if (Double.TryParse(testVar, out myNum)) {
// it is a number
} else {
// it is not a number
}
Incidentally, all of the standard data types, with the glaring exception of GUIDs, support TryParse.
update
secretwep brought up that the value "2345," will pass the above test as a number. However, if you need to ensure that all of the characters within the string are digits, then another approach should be taken.
example 1:
public Boolean IsNumber(String s) {
Boolean value = true;
foreach(Char c in s.ToCharArray()) {
value = value && Char.IsDigit(c);
}
return value;
}
or if you want to be a little more fancy
public Boolean IsNumber(String value) {
return value.All(Char.IsDigit);
}
update 2 ( from #stackonfire to deal with null or empty strings)
public Boolean IsNumber(String s) {
Boolean value = true;
if (s == String.Empty || s == null) {
value=false;
} else {
foreach(Char c in s.ToCharArray()) {
value = value && Char.IsDigit(c);
}
} return value;
}
I prefer something like this, it lets you decide what NumberStyle to test for.
public static Boolean IsNumeric(String input, NumberStyles numberStyle) {
Double temp;
Boolean result = Double.TryParse(input, numberStyle, CultureInfo.CurrentCulture, out temp);
return result;
}
In addition to the previous correct answers it is probably worth pointing out that "Not a Number" (NaN) in its general usage is not equivalent to a string that cannot be evaluated as a numeric value. NaN is usually understood as a numeric value used to represent the result of an "impossible" calculation - where the result is undefined. In this respect I would say the Javascript usage is slightly misleading. In C# NaN is defined as a property of the single and double numeric types and is used to refer explicitly to the result of diving zero by zero. Other languages use it to represent different "impossible" values.
I know this has been answered in many different ways, with extensions and lambda examples, but a combination of both for the simplest solution.
public static bool IsNumeric(this String s)
{
return s.All(Char.IsDigit);
}
or if you are using Visual Studio 2015 (C# 6.0 or greater) then
public static bool IsNumeric(this String s) => s.All(Char.IsDigit);
Awesome C#6 on one line. Of course this is limited because it just tests for only numeric characters.
To use, just have a string and call the method on it, such as:
bool IsaNumber = "123456".IsNumeric();
Yeah, IsNumeric is VB. Usually people use the TryParse() method, though it is a bit clunky. As you suggested, you can always write your own.
int i;
if (int.TryParse(string, out i))
{
}
I like the extension method, but don't like throwing exceptions if possible.
I opted for an extension method taking the best of 2 answers here.
/// <summary>
/// Extension method that works out if a string is numeric or not
/// </summary>
/// <param name="str">string that may be a number</param>
/// <returns>true if numeric, false if not</returns>
public static bool IsNumeric(this String str)
{
double myNum = 0;
if (Double.TryParse(str, out myNum))
{
return true;
}
return false;
}
You can still use the Visual Basic function in C#. The only thing you have to do is just follow my instructions shown below:
Add the reference to the Visual Basic Library by right clicking on your project and selecting "Add Reference":
Then import it in your class as shown below:
using Microsoft.VisualBasic;
Next use it wherever you want as shown below:
if (!Information.IsNumeric(softwareVersion))
{
throw new DataException(string.Format("[{0}] is an invalid App Version! Only numeric values are supported at this time.", softwareVersion));
}
Hope, this helps and good luck!
VB has the IsNumeric function. You could reference Microsoft.VisualBasic.dll and use it.
Simple extension:
public static bool IsNumeric(this String str)
{
try
{
Double.Parse(str.ToString());
return true;
}
catch {
}
return false;
}
public static bool IsNumeric(string anyString)
{
if (anyString == null)
{
anyString = "";
}
if (anyString.Length > 0)
{
double dummyOut = new double();
System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo("en-US", true);
return Double.TryParse(anyString, System.Globalization.NumberStyles.Any, cultureInfo.NumberFormat, out dummyOut);
}
else
{
return false;
}
}
Maybe this is a C# 3 feature, but you could use double.NaN.
Actually, Double.NaN is supported in all .NET versions 2.0 and greater.
I was using Chris Lively's snippet (selected answer) encapsulated in a bool function like Gishu's suggestion for a year or two. I used it to make sure certain query strings were only numeric before proceeding with further processing. I started getting some errant querystrings that the marked answer was not handling, specifically, whenever a comma was passed after a number like "3645," (returned true). This is the resulting mod:
static public bool IsNumeric(string s)
{
double myNum = 0;
if (Double.TryParse(s, out myNum))
{
if (s.Contains(",")) return false;
return true;
}
else
{
return false;
}
}
This is a modified version of the solution proposed by Mr Siir. I find that adding an extension method is the best solution for reuse and simplicity in the calling method.
public static bool IsNumeric(this String s)
{
try { double.Parse(s); return true; }
catch (Exception) { return false; }
}
I modified the method body to fit on 2 lines and removed the unnecessary .ToString() implementation. For those not familiar with extension methods here is how to implement:
Create a class file called ExtensionMethods. Paste in this code:
using System;
using System.Collections.Generic;
using System.Text;
namespace YourNameSpaceHere
{
public static class ExtensionMethods
{
public static bool IsNumeric(this String s)
{
try { double.Parse(s); return true; }
catch (Exception) { return false; }
}
}
}
Replace YourNameSpaceHere with your actual NameSpace. Save changes. Now you can use the extension method anywhere in your app:
bool validInput = stringVariable.IsNumeric();
Note: this method will return true for integers and decimals, but will return false if the string contains a comma. If you want to accept input with commas or symbols like "$" I would suggest implementing a method to remove those characters first then test if IsNumeric.
I have a slightly different version which returns the number. I would guess that in most cases after testing the string you would want to use the number.
public bool IsNumeric(string numericString, out Double numericValue)
{
if (Double.TryParse(numericString, out numericValue))
return true;
else
return false;
}
If you don't want the overhead of adding the Microsoft.VisualBasic library just for isNumeric, here's the code reverse engineered:
public bool IsNumeric(string s)
{
if (s == null) return false;
int state = 0; // state 0 = before number, state 1 = during number, state 2 = after number
bool hasdigits = false;
bool hasdollar = false;
bool hasperiod = false;
bool hasplusminus = false;
bool hasparens = false;
bool inparens = false;
for (var i = 0; i <= s.Length - 1; i++)
{
switch (s[i])
{
case char n when (n >= '0' && n <= '9'):
if (state == 2) return false; // no more numbers at the end (i.e. "1 2" is not valid)
if (state == 0) state = 1; // begin number state
hasdigits = true;
break;
case '-':
case '+':
// a plus/minus is allowed almost anywhere, but only one, and you cannot combine it with parenthesis
if (hasplusminus || hasparens) return false;
if (state == 1) state = 2; // exit number state (i.e. "1-" is valid but 1-1 is not)
hasplusminus = true;
break;
case ' ':
case '\t':
case '\r':
case '\n':
// don't allow any spaces after parenthesis/plus/minus, unless there's a $
if (state == 0 && (hasparens || (hasplusminus && !hasdollar))) return false;
if (state == 1) state = 2; // exit number state
break;
case ',':
// do not allow commas unless in the middle of the number, and not after a decimal
if (state != 1 || hasperiod) return false;
break;
case '.':
// only allow one period in the number
if (hasperiod || state == 2) return false;
if (state == 0) state = 1; // begin number state
hasperiod = true;
break;
case '$':
// dollar symbol allowed anywhere, but only one
if (hasdollar) return false;
if (state == 1) state = 2; // exit number state (i.e. "1$" is valid but "1$1" is not)
hasdollar = true;
break;
case '(':
// only allow one parens at the beginning, and cannot combine with plus/minus
if (state != 0 || hasparens || hasplusminus) return false;
hasparens = true;
inparens = true;
break;
case ')':
if (state == 1 && inparens) state = 2; // exit number state
if (state != 2 || !inparens) return false; // invalid end parens
inparens = false; // end parens mode
break;
default:
// oh oh, we hit a bad character
return false;
}
}
// must have at leats one digit, and cannot have imbalanced parenthesis
if (!hasdigits || inparens) return false;
// if we got all the way to here...
return true;
}

Categories