Why is TryParse the way round that it is? - c#

I've been struggling to get my head around a natural way of using TryParse because I keep expecting it to work the other way around (i.e. to return the parsed value and emit the boolean for whether the input parsed).
For example, if we take a basic implementation of Parse, the return value is the parsed input:
int parsedValue = int.Parse(input);
This works fine until it gets a value that it can't parse, at which point it entirely reasonably throws an exception. To me, the option then is either to wrap the parse in something like a try-catch block to handle the exception and a condition to set a default, or just to use TryParse to let C# do all that for me. Except that's not how TryParse works. The above example now looks like this:
bool parseSucceeded = int.TryParse(input, out int parsedValue);
To get it to assign in the same way as Parse, I wrap it in a ternary conditional with parsedValue and a default value (in this case, 0) as the true and false results respectively:
int parsedValue = int.TryParse(input, out parsedValue) ? parsedValue : 0;
But I still feel like I'm missing the point with TryParse if I'm just working around its default behaviour like this. I've read Tim Schmelter's excellent answer in which he shows its internal workings, from which I can suppose that it returns the boolean because it's easier internally than passing it out at all the various places that it currently returns. But I'm not sure about this, and I'm not satisfied that I understand its intent correctly. I also tried reading the documentation for it, but the remarks don't clear up my confusion (I don't think they even make its differences with Parse clear enough, like the change in return type).
Am I understanding it correctly, or am I missing something?

Sure, it could have been implemented as
int TryParse(string input, out bool succeeded)
{
}
But as mentioned in a comment, the common use case for the function is:
string input;
int parsedValue;
if(int.TryParse(input, out parsedValue))
{
// use parsedValue here
}
With the signature you propose, that code would now be:
string input;
bool succeeded;
int parsedValue = int.TryParse(input, out succeeded)
if(succeeded)
{
// use parsedValue here
}
So there's more code for no functional benefit. Also, with your ternary operator, if the parse fails you just set a value of zero, which is unnecessary since the default value of it is 0. You could just do:
int parsedValue;
int.TryParse(input, out parsedValue);
If the parse fails, parsedValue will have a value of 0; (I also question if/how you distinguish between an actual result of 0 and a failed parse, but I'm sure you have a reason).
So there's no technical reason why the signature is the way it is; it's a design decision that is appropriate for the most common use cases.
Of course, now with tuples in C# 7 you could have:
(int parsedValue, bool succeeded) = int.TryParse(input);
but again there's little functional benefit and prevents you from inlining the TryParse in an if statement.

Because logically you would want to check that the TryParse succeeded before trying to use the out value.
So this is more concise:
if (int.TryParse(input, out int parsedValue)}
{
// Do something with parsedValue
}
Than this:
int parsedValue = int.TryParse(input, out bool succeded);
if (succeeded)
{
// Do something with parsedValue
}

I think, a large part of your confusion stems from the method name isn't named exactly right:
int parsedValue = int.Parse("42");
This makes perfect sense, give me the integeger representation of a string.
int parsedValue = int.TryParse(input);
This makes sense as an extension of the concept: Input might be '42' or 'Oswald', but if it's a number I want that number.
In 2020, I think a better name would be CanParse(string input, out int result).
It better matches style guides and naming conventions, where returning a bool should be named with Is, Has, or Can.
It better matches how we use TryParse 99% of the time:
if (int.CanParse(input, out int result))
{
return result * 10;
}
But where I feel the current name makes sense, is the problem I assume it was trying to solve: To get rid of the following boilerplate code:
int result;
bool hasValidNumber = false;
try
{
result = int.Parse(input);
hasValidNumber = true;
}
catch
{
// swallow this exception
}
if (hasValidNumber)
{
// do things with result
}
else
{
// use a default or other logic
}

Related

Comparing a String value to ALL values in an Enum in C# using TryParse - Logic Issue

I'm trying to determine if a string value read-in from a file is found within an enum in my code.
The enum:
internal enum Difficulty { None, Easy, Medium, Hard }
At this point in my code header[i] = "EASY".
I want to compare this value to any of the values found in my Difficulty enum but am encountering issues.
The code:
Each time the following code is executed, the if statement returns false because "Easy" != "EASY".
Difficulty dif = Difficulty.None;
if (Difficulty.TryParse(header[i], out dif)) { // RETURNS FALSE
MyLog.Write("It's in the Enum!");
}
I've tried comparing the header[i] value to the Difficulty value in the TryParse statement, but it results in a compiler error.
Is there anything I can do besides changing all the Difficulty values to uppercase ones?
Use the version of TryParse that supports ignoring case: https://msdn.microsoft.com/en-us/library/dd991317(v=vs.110).aspx
Your code becomes:
Difficulty dif = Difficulty.None;
if (Enum.TryParse<Difficulty>(header[i], true, out dif))
{
MyLog.Write("It's in the Enum!");
}
The .ToString value for any Enum returns just the name your provide for it. So if you're gonna be doing this in a loop i suggest getting all the names to a local list and search in that which would result in better performance:
string[] enumNames = Enum.GetNames(typeof(Difficulty));
bool found = enumNames.Any(x => x.Equals(header[i], StringComparison.OrdinalIgnoreCase));
Yes there is!
Use the overload for Enum.TryParse that allows you to ignore the case:
if (Enum.TryParse("EASY", true, out dif))
{
Console.WriteLine("It's in the Enum!");
}

Having trouble casting string to integer

I'm trying to convert the string "127.0" to an integer.
I tried this function:
int getInt(string numStr)
{
int result;
int.TryParse(numStr, out result);
return result;
}
But when I call it as int x = getInt("127.0"); then int.TryParse() sets result to 0.
When I rewrite the function like this:
int getInt(string numStr)
{
result=Convert.ToInt32(numStr);
return result;
}
the same getInt() call throws this exception:
Input string was not in a correct format.
The issue here is that "127.0" is not an integer, it's a floating point number. You will need to parse it using one of the other floating point types (i.e. double, float, Decimal, etc.).
You may want to consider either stripping off any values after the decimal point and attempting to parse it, or parsing it as another type and casting it as an integer :
int result = (int)Convert.ToDouble("1.270");
You could also take advantage of the Math.Truncate() function which would give you the integer portion of your value :
int result = (int)Math.Truncate(Convert.ToDouble("127.0"));
First off, you need to check the return value of int.TryParse(). If it returns false, then the string could not be converted.
Had you done that, you would see it returned false because 127.0 does not describe an integer value (it describes a floating point value).
Note that decimal.TryParse() would succeed here. You need to figure out if you need an integer or floating point value, and reject data that is incorrect.
An int cannot contain a decimal point; that makes it either a double, a float, or a decimal. Try to pull the number minus anything from the decimal point over to the right, like this:
int getInt(string numStr)
{
int result;
string[] splitup;
string number;
if (numstr.Contains('.'))
{
splitup = numstr.Split('.');
number = splitup[0];
int.TryParse(number, out result);
}
else
{
int.TryParse(numstr, out result);
}
return result;
}
Rion Williams is absolutely correct, IMHO.
Along with the fact that what you are parsing is not an integer, I'd personally use the TryParse method. Many of the .NET types have it, and it's quite a bit "safer" (it won't throw exceptions) than just parsing a string.
Example:
string stringValue = "127.0";
int intValue;
if(Int32.TryParse(stringValue, out intValue))
{
// return value
}
// handle the failure
If you don't like that, I'd wrap it in a try-catch...

Trying to parse a string in C# using the TryParse helper method

I'm trying to parse user entered text into a float so that I can perform some wicked awesome math on the result. The result being the numberA variable at the end there. This is how I'm parsing the string into a float:
numberAString = GUI.TextField(new Rect(25, 100, 100, 25), numberAString.ToString());
bool ParsableCheckA = float.TryParse(numberAString, out numberA);
I eventually multiply numberA by another float later. I'm handling text that won't parse with a simple error message later. Couple things that bug me:
1) Why do I need to use numberAString in the TryParse parameters instead of its value? Why can't I just drop GUI.Textfield etc. into that slot? Why do I need to break this up over two lines of code?
2) I get a warning that I never use ParsableCheckA (which is true). But without it, I can't seem to use that Tryparse helper, no? Is there a way to eliminate the need for that bool? Thanks.
TryParse() Metod and its usage is rather straighforward, as shown in the following example:
double _dblOriginal;
_dblOriginal=3.141592;
string _str;
_str = _dblOriginal.ToString();
double _dblParsed;
bool _parseOK= TryParse(_str, out dblParsed);
More details in: http://msdn.microsoft.com/en-us/library/system.double.tryparse%28v=vs.110%29.aspx
Note: make sure that in your example the string numberAString you pass to the TryParse() method contains a valid number.
Also, FYI: TryParse() method has some performance benefits compare to just Parse() method because it does not throw an exception if parsing fail (instead you just check the bool result value).
Hope this will help. Regards,
(1) numberAString (see GUI.TextField) is a string, so you can use it directly in the TryParse() call.
(2) TryParse() is designed to return a boolean value so you can check for errors and take action. However, you aren't required to assign the return value. You can us it like this and simply accept the default value (0.0) assigned to the out parameter:
float.TryParse(numberAString, out numberA);
In this case you would not be able to distinguish between an invalid entry and a value that parsed correctly as zero.
I personally create an extension method to make my number parsing easier. If performance is critical, then this may not be a good idea (because for valid floats, you'll be essentially converting them twice). But I don't the effect on performance is much more than negligible.
public static class NumberExtensions
{
//Test if the text represents a valid float.
public static bool IsFloat(this string text)
{
float dummy = 0;
return Float.TryParse(text, out dummy);
}
//Convert the text to a float. Will throw exception if it's not a valid float.
public static float ToFloat(this string text)
{
float number = Float.Parse(text);
return number;
}
}
Usage...
string text = "123";
if(text.IsFloat())
{
//text must be a valid float
float myfloat = text.ToFloat();
}
else
{
//text isn't a valid float
}
You have to remember to test if it's a float by calling the IsFloat() extension method, but it's much easier for me to think of it conceptually like this rather than using out variables, which some think is a bad idea.

Lack of IsNumeric function in C#

One thing that has bothered me about C# since its release was the lack of a generic IsNumeric function. I know it is difficult to generate a one-stop solution to detrmine if a value is numeric.
I have used the following solution in the past, but it is not the best practice because I am generating an exception to determine if the value is IsNumeric:
public bool IsNumeric(string input)
{
try
{
int.Parse(input);
return true;
}
catch
{
return false;
}
}
Is this still the best way to approach this problem or is there a more efficient way to determine if a value is numeric in C#?
Try this:
int temp;
return int.TryParse(input, out temp);
Of course, the behavior will be different from Visual Basic IsNumeric. If you want that behavior, you can add a reference to "Microsoft.VisualBasic" assembly and call the Microsoft.VisualBasic.Information.IsNumeric function directly.
You can use extension methods to extend the String type to include IsInteger:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static bool IsInteger(this String input)
{
int temp;
return int.TryParse(input, out temp);
}
}
}
Rather than using int.Parse, you can use int.TryParse and avoid the exception.
Something like this
public static bool IsNumeric(string input)
{
int dummy;
return int.TryParse(input, out dummy);
}
More generically you might want to look at double.TryParse.
One thing you should also consider is the potential of handling numeric string for different cultures. For example Greek (el-GR) uses , as a decimal separator while the UK (en-GB) uses a .. So the string "1,000" will either be 1000 or 1 depending on the current culture. Given this, you might consider providing overloads for IsNumeric that support passing the intended culture, number format etc. Take a look at the 2 overloads for double.TryParse.
I've used the following extension method before, if it helps at all:
public static int? AsNumeric(this string source)
{
int result;
return Int32.TryParse(source, out result) ? result : (int?)null;
}
Then you can use .HasValue for the bool you have now, or .Value for the value, but convert just once...just throwing it out there, not sure what situation you're using it for afterwards.
If you use Int32.TryParse then you don't need to wrap the call in a TryCatch block, but otherwise, yes that is the approach to take.
Not exactly crazy about this approach, but you can just call the vb.net isNumeric function from C# by adding a reference to the Microsoft.VisualBasic.dll library...
bool x= Microsoft.VisualBasic.Information.IsNumeric("123");
The other approaches given are superior, but wanted to add this for the sake of completeness.
Lot's of TryParse answers. Here's something a bit different using Char.IsNumber():
public bool IsNumeric(string s)
{
for (int i = 0; i < s.Length; i++)
{
if (char.IsNumber(s, i) == false)
{
return false;
}
}
return true;
}
Take a look on the following answer:
What is the C# equivalent of NaN or IsNumeric?
Double.TryParse takes care of all numeric values and not only ints.
Another option - LINQ!
public static class StringExtensions
{
public static bool IsDigits(this String text)
{
return !text.Any(c => !char.IsDigit(c));
}
}
Note that this assumes you only want digits 0-9. If you want to accept decimal point, sign, exponent, etc, then repalce IsDigit() with IsNumber().
I've been using the following small code snippet for years as a pure C# IsNumeric function.
Granted, it's not exactly the same as the Microsoft.VisualBasic library's IsNumeric function as that (if you look at the decompiled code) involves lots of type checking and usage of the IConvertible interface, however this small function has worked well for me.
Note also that this function uses double.TryParse rather than int.TryParse to allow both integer numbers (including long's) as well as floating point numbers to be parsed. Also note that this function specifically asserts an InvariantCulture when parsing (for example) floating point numbers, so will correctly identify both 123.00 and 123,00 (note the comma and decimal point separators) as floating point numbers.
using System;
using System.Globalization;
namespace MyNumberFunctions
{
public static class NumberFunctions
{
public static bool IsNumeric(this object expression)
{
if (expression == null)
{
return false;
}
double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
}
}
}
Usage is incredibly simple, since this is implemented as an extension method:
string myNumberToParse = "123.00";
bool isThisNumeric = myNumberToParse.IsNumeric();
public bool IsNumeric(string input)
{
int result;
return Int32.TryParse(input,out result);
}
try this:
public static bool IsNumeric(object o)
{
const NumberStyles sty = NumberStyles.Any;
double d;
return (o != null && Double.TryParse(o.ToString(), sty, null, out d));
}
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!

.NET testing a string for numeric value

Consider the need for a function in C# that will test whether a string is a numeric value.
The requirements:
must return a boolean.
function should be able to allow for whole numbers, decimals, and negatives.
assume no using Microsoft.VisualBasic to call into IsNumeric(). Here's a case of reinventing the wheel, but the exercise is good.
Current implementation:
//determine whether the input value is a number
public static bool IsNumeric(string someValue)
{
Regex isNumber = new Regex(#"^\d+$");
try
{
Match m = isNumber.Match(someValue);
return m.Success;
}
catch (FormatException)
{return false;}
}
Question: how can this be improved so that the regex would match negatives and decimals? Any radical improvements that you'd make?
Just off of the top of my head - why not just use double.TryParse ? I mean, unless you really want a regexp solution - which I'm not sure you really need in this case :)
Can you just use .TryParse?
int x;
double y;
string spork = "-3.14";
if (int.TryParse(spork, out x))
Console.WriteLine("Yay it's an int (boy)!");
if (double.TryParse(spork, out y))
Console.WriteLine("Yay it's an double (girl)!");
Regex isNumber = new Regex(#"^[-+]?(\d*\.)?\d+$");
Updated to allow either + or - in front of the number.
Edit: Your try block isn't doing anything as none of the methods within it actually throw a FormatException. The entire method could be written:
// Determine whether the input value is a number
public static bool IsNumeric(string someValue)
{
return new Regex(#"^[-+]?(\d*\.)?\d+$").IsMatch(someValue);
}
Well, for negatives you'd need to include an optional minus sign at the start:
^-?\d+$
For decimals you'd need to account for a decimal point:
^-?\d*\.?\d*$
And possible exponential notation:
^-?\d*\.?\d*(e\d+)?$
I can't say that I would use regular expressions to check if a string is a numeric value. Slow and heavy for such a simple process. I would simply run over the string one character at a time until I enter an invalid state:
public static bool IsNumeric(string value)
{
bool isNumber = true;
bool afterDecimal = false;
for (int i=0; i<value.Length; i++)
{
char c = value[i];
if (c == '-' && i == 0) continue;
if (Char.IsDigit(c))
{
continue;
}
if (c == '.' && !afterDecimal)
{
afterDecimal = true;
continue;
}
isNumber = false;
break;
}
return isNumber;
}
The above example is simple, and should get the job done for most numbers. It is not culturally sensitive, however, but it should be strait-forward enough to make it culturally sensitive.
Also, make sure the resulting code passes the Turkey Test:
http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html
Unless you really want to use regex, Noldorin posted a nice extension method in another Q&A.
Update
As Patrick rightly pointed out, the link points to an extension method that check whether the object is a numeric type or not, not whether it represents a numeric value. Then using double.TryParse as suggested by Saulius and yodaj007 is probably the best choice, handling all sorts of quirks with different decimal separators, thousand separators and so on. Just wrap it up in a nice extension method:
public static bool IsNumeric(this string value)
{
double temp;
return double.TryParse(value.ToString(), out temp);
}
...and fire away:
string someValue = "89.9";
if (someValue.IsNumeric()) // will be true in the US, but not in Sweden
{
// wow, it's a number!
]

Categories