4

I am seeing lots of these in a method in our code:

int num1 = 0;
if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
    valid = false;
}

So are they just making sure the third character us a digit?

  • Maybe I'm missing something obvious but i can't work out what value for `myStr[2]` would cause `Char.IsDigit(myStr[2])` to be true and `Int32.TryParse(myStr[2].ToString(), out num1)` to be false. I.E. It is a digit between 0 and 9 but cannot be parsed as an integer. – Ben Robinson Oct 10 '14 at 16:02
  • @BenRobinson see the answers – thumbmunkeys Oct 10 '14 at 16:02
  • What is the type of `myStr` ? is it a `string`? – Marc Gravell Oct 10 '14 at 16:03
  • it is worth noting that IsDigit returns true for all unicode digits, not just for latin (0-9) digits.. so even if IsDigit returns true, TryParse might fail.. – Selman Genç Oct 10 '14 at 16:08

6 Answers6

7

The code shown parses the 3rd character only - checking if it is digit, then parsing the string representation of that single character. Instead, just use the numeric value of that character:

if(myStr[2] >= '0' && myStr[2] <= '9') {
    num1 = (int)myStr[2] - (int)'0';
} else {
    valid = false
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    I would even go far as to say this is most likely the absolute fastest (ignoring the possible OutOfRangeException). – Erik Philips Oct 11 '14 at 01:52
5

You can safely skip the IsDigit() check as it's redundant.

TryParse() will fail if it's not a digit.

As it has been pointed out by others, Char.IsDigit() is quicker. If your code is performance sensitive the check makes sense.

If you leave the IsDigit check in place, then you can reduce TryParse to Int32.Parse() as at that point the parsing won't fail.

thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110
  • Maybe `TryParse` is the part that's redundant, especially if you're not using `num1` for anything. – Matthew Oct 10 '14 at 15:53
  • @Matthew: That's a big assumption from just that one snippet of code. – Matt Burland Oct 10 '14 at 15:54
  • 1
    @Matthew yes, maybe. I can't tell from the context. – thumbmunkeys Oct 10 '14 at 15:54
  • @MattBurland I'm not assuming anything, it's up to the viewer to decide what's appropriate. – Matthew Oct 10 '14 at 15:57
  • @Matthew: You kind of are. The only thing we can safely say is redundant is the `IsDigit` check since it has no side-effects. Of course, as dasblinkenlight says in their answer, it's probably the original programmer trying to optimize by doing a (presumed) cheaper `IsDigit` check before a more expensive `TryParse`. – Matt Burland Oct 10 '14 at 15:59
  • @thumbmunkeys I think `Char.IsDigit` or `TryParse` is not redundant... See my answer and tell me if it makes sense... – Syed Farjad Zia Zaidi Oct 10 '14 at 16:41
  • @SyedFarjadZiaZaidi good point, but my answer that `Char.IsDigit` is redundant is still valid, as the `TryParse` is stricter. But you are right when you say that they are not exchangable. There is a difference between them. – thumbmunkeys Oct 10 '14 at 16:55
4

It looks like the code that you have is doing this for efficiency. Whoever coded this, knows the structure of the string in myStr to sometimes have a non-numeric symbol in the third position. That's why he made this optimization to check the third symbol before paying for the conversion of the character array to string which then gets parsed.

Chances are, this optimization is premature: although making a temporary throw-away string is not free, this optimization would make sense only in situations when you do it a lot in a very tight loop. In other words, you do it only if it shows up near the top in your performance profiler's output.

You can optimize this check to avoid if:

int num1 = 0;
valid &= !Char.IsDigit(myStr[2]) || Int32.TryParse(myStr[2].ToString(), out num1);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
3

I don't believe you need the first part (it could also throw an IndexOutOfRangeException).

So I would probably use:

int num1 = 0;
if (myStr.Length > 2 && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
  valid = false;
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
1

Char.IsDigit Method (String, Int32)

Indicates whether the character at the specified position in a specified string is categorized as a decimal digit.

Link

Int32.TryParse Method

Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the operation succeeded. This member is overloaded.

Link

Edit:

First I wrote that you can skip any of the check but now I am writing that you can not because

if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{ }

Char.IsDigit() will return true if myStr[2] contains any of the Unicode characters listed here but Int.TryParse() will not convert any numbers except for 0-9 (not sure about this, as I have not checked all of them) so it will return false which is you are checking...

The condition you are checking can be understood by the following example:

string x = "AS௭s";
int s = 0;
if (Char.IsDigit(x[2]) && int.TryParse(x[2].ToString(), out s) == false)
{
    // even if '௭` is Tamil Digit Seven and 'Char.IsDigit()' will return true but 
    // int.TryParse() will return false because it can not convert it
    // so you are setting valid = false when the myStr contains a valid Unicode Character
    // for a digit but It can not be converted to integer by TryParse method...
    valid = false;
}

@Marc Gravell♦'s answer is the best solution for checking this condition...

Community
  • 1
  • 1
Syed Farjad Zia Zaidi
  • 3,302
  • 4
  • 27
  • 50
-3

Here's how I'd write it:

int num1 = 0;
try
{
    num1 = Int32.Parse(myStr[2].ToString());
}
catch (Exception)
{
    valid = false;
}

This does the same thing and is a lot easier to read imho, oh & you can log failed parses inside the catch.

Or you can do:

int num1 = 0;
valid = Int32.TryParse(myStr[2].ToString(), out num1);
RandomUs1r
  • 4,010
  • 1
  • 24
  • 44
  • 1
    This would unconditionally change `valid`, while the original example keeps the old value when the third character is not a digit. Moreover, `TryParse` has been introduced to help you avoid using the more expensive `try`/`catch` in your code. – Sergey Kalinichenko Oct 10 '14 at 15:59
  • I guess we would need to know the intent behind valid, for 99% of scenarios setting valid to true on success is perfectly legit & doesn't involve duplicating Boolean logic. Try catch is more expensive sure (nobody would ever notice I bet), but as I said in my answer, it's more for readability. – RandomUs1r Oct 10 '14 at 16:20
  • As a general rule, you should never use exceptions for program flow. – thumbmunkeys Oct 10 '14 at 17:00
  • @thumbmunkeys why not? I can think of a few legit scenarios, 3rd party web services being the first to come to mind. – RandomUs1r Oct 10 '14 at 18:49
  • Also to add some value to my answer, TryParse looks like this: http://stackoverflow.com/questions/15294878/how-the-int-tryparse-actually-works . Nor is it threadsafe. – RandomUs1r Oct 10 '14 at 18:53
  • @RandomUs1r see here http://programmers.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why – thumbmunkeys Oct 10 '14 at 22:47
  • @thumbmunkeys nice link, most likely applies to this situation on why not to use try catch here, in the example @ teleco however it is perfectly valid. Thus, please direct your attention to my 2nd two line solution that doesn't suck. – RandomUs1r Oct 13 '14 at 22:03