I used to teach in a technology program at ODU (until the university sold the program . . . but that's another story); I still get questions emailed from students about the “right” way to program things etc. The latest question was about the lack of an “IsNumeric” function in C#; as you probably know, VB.Net has an IsNumeric() function that returns a boolean for if a number is numeric or not. While VB.Net's IsNumeric() function is hardly rocket science, it is a very useful function for testing your strings.
Anyway, C# is a little more “roll-up-your-sleeves” and solve your own problem, so there is no equivalent IsNumeric function in C#. This former student of mine asked if the following was an acceptable alternative:
public static bool isNumeric( string strText )
{
try
{
int i = int.Parse( strText ) ;
return true ;
}
catch
{
return false ;
}
}
What's wrong with this picture? This is classic lazy-programming (or, in this case, just inexperienced programming) because the person knows enough about C# to use try{}catch{}, but is using it inappropriately. Exceptions are for exceptional situations -- not for avoiding some real thinking through the issue. Catching exceptions is hard work on your programs and performance will really suffer: this try{}catch{} isNumeric() function is over 2,000 times slower than this alternative:
public static bool isNumeric( string strText )
{
char[] chars = strText.ToCharArray() ;
for ( int i = 0; i < chars.Length; i++ )
{
if( !Char.IsDigit( chars[ i ] ) ) //could also explore IsNumber
{
return false ;
}
}
return true ;
}
The good ol' Char class has a baked in IsDigit() function that will also handle localized definitions of numbers; this version of isNumeric() will work with Cyrillic defined numbers as well as our “conventional” numerals -- assuming the system is setup for the appropriate region/locale. This function requires a bit more typing, but iterating over every character in a string and testing for that character's “IsDigit-ness” is 2,000 times faster than the try{}catch{} solution. Of course, your mileage may vary based on the length of the string you're comparing etc (I used strings of 10 characters in my tests).
If blinding perf is what you're after, this is the alternative isNumeric function you want:
public static bool isNumeric( string strText )
{
char[] chars = strText.ToCharArray() ;
for ( int i = 0; i < chars.Length; i++ )
{
if ( chars[ i ] > 57 || chars[ i ] < 48 )
{
return false ;
}
}
return true ;
}
You lose the universal comparison logic built into Char.IsDigit(), but this is over twice as fast as the previous alternative (and fully 5000+ times faster than the original try{}catch{} function). Exact statistics are at the bottom of this post. If your app will only run on machines where characters 48-57 will be numeric -- like with our western number system -- this is the moet et chandon of the IsNumeric() world.
Yes, you can call into the VB.Net library to get to that IsNumeric() function; this will be slow, too. Besides, I know some C# die-hards that would rather eat their own young than call a VB.Net library intentionally. I've also omitted a Regex isNumeric() alternative and a few others, but I didn't set out to write the definitive study on isNumeric for C#. Originally, I wanted to demonstrate that the “quick-and-dirty” try{}catch{} coding solution to this problem is for those who want to CodeWorse -- and since my blog is now on www.CodeBetter.com I figured I better get with the program!
So, using perf monitoring like in this post and a little ILDasm:
- The try{}catch{} CodeWorse approach took 43 seconds to run through 30,000 test calls to IsNumeric where 66% of the strings were not numeric (meaning, 66% return false). That's a lot of exceptions! The IL for the function has 2 calls into MSCorLib and the perf-crushing IL try-catch handlers.
- The Char.IsDigit approach took only .0171 seconds to run through the same 30,000 test calls. This approach still has 2 calls into MSCorlib in the IL, but none of the try-catch handlers to cause pain and perf agony.
- The simple character comparison array loop was the fastest for these 30,000 calls at .0078 seconds. Only 1 MSCorLib call. No try-catch nonsense.
Can you hear me in the back? The time it takes the try{}catch{} “solution” to work in a loop situation can be measured in whole seconds (perhaps minutes!) while the other approaches only take fractions of fractions of seconds. I can literally type the entire “verbose” character comparison array loop solution in Visual Studio in the time it takes the try{}catch{} “solution” to run -- so don't give me any “quick and dirty is fine” stuff here. It's just inexperience or laziness that would have you CodingWorse with try{}catch{} blocks.