How the Int.Tryparse Actually Works

How the int.TryParse actually works

If you only need the bool result, just use the return value and ignore the out parameter.

bool successfullyParsed = int.TryParse(str, out ignoreMe);
if (successfullyParsed){
// ...
}

Edit: Meanwhile you can also have a look at the original source code:

System.Int32.TryParse


If i want to know how something is actually implemented, i'm using ILSpy to decompile the .NET-code.

This is the result:

// int
/// <summary>Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the operation succeeded.</summary>
/// <returns>true if s was converted successfully; otherwise, false.</returns>
/// <param name="s">A string containing a number to convert. </param>
/// <param name="result">When this method returns, contains the 32-bit signed integer value equivalent to the number contained in s, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the s parameter is null, is not of the correct format, or represents a number less than <see cref="F:System.Int32.MinValue"></see> or greater than <see cref="F:System.Int32.MaxValue"></see>. This parameter is passed uninitialized. </param>
/// <filterpriority>1</filterpriority>
public static bool TryParse(string s, out int result)
{
return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}

// System.Number
internal unsafe static bool TryParseInt32(string s, NumberStyles style, NumberFormatInfo info, out int result)
{
byte* stackBuffer = stackalloc byte[1 * 114 / 1];
Number.NumberBuffer numberBuffer = new Number.NumberBuffer(stackBuffer);
result = 0;
if (!Number.TryStringToNumber(s, style, ref numberBuffer, info, false))
{
return false;
}
if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
{
if (!Number.HexNumberToInt32(ref numberBuffer, ref result))
{
return false;
}
}
else
{
if (!Number.NumberToInt32(ref numberBuffer, ref result))
{
return false;
}
}
return true;
}

And no, i cannot see any Try-Catchs on the road:

// System.Number
private unsafe static bool TryStringToNumber(string str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
if (str == null)
{
return false;
}
fixed (char* ptr = str)
{
char* ptr2 = ptr;
if (!Number.ParseNumber(ref ptr2, options, ref number, numfmt, parseDecimal) || ((ptr2 - ptr / 2) / 2 < str.Length && !Number.TrailingZeros(str, (ptr2 - ptr / 2) / 2)))
{
return false;
}
}
return true;
}

// System.Number
private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
number.scale = 0;
number.sign = false;
string text = null;
string text2 = null;
string str2 = null;
string str3 = null;
bool flag = false;
string str4;
string str5;
if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
{
text = numfmt.CurrencySymbol;
if (numfmt.ansiCurrencySymbol != null)
{
text2 = numfmt.ansiCurrencySymbol;
}
str2 = numfmt.NumberDecimalSeparator;
str3 = numfmt.NumberGroupSeparator;
str4 = numfmt.CurrencyDecimalSeparator;
str5 = numfmt.CurrencyGroupSeparator;
flag = true;
}
else
{
str4 = numfmt.NumberDecimalSeparator;
str5 = numfmt.NumberGroupSeparator;
}
int num = 0;
char* ptr = str;
char c = *ptr;
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowLeadingWhite) == NumberStyles.None || ((num & 1) != 0 && ((num & 1) == 0 || ((num & 32) == 0 && numfmt.numberNegativePattern != 2))))
{
bool flag2;
char* ptr2;
if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == '(' && (options & NumberStyles.AllowParentheses) != NumberStyles.None && (num & 1) == 0)
{
num |= 3;
number.sign = true;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
num |= 32;
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
int num2 = 0;
int num3 = 0;
while (true)
{
if ((c >= '0' && c <= '9') || ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))))
{
num |= 4;
if (c != '0' || (num & 8) != 0)
{
if (num2 < 50)
{
number.digits[(IntPtr)(num2++)] = c;
if (c != '0' || parseDecimal)
{
num3 = num2;
}
}
if ((num & 16) == 0)
{
number.scale++;
}
num |= 8;
}
else
{
if ((num & 16) != 0)
{
number.scale--;
}
}
}
else
{
char* ptr2;
if ((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None && (num & 16) == 0 && ((ptr2 = Number.MatchChars(ptr, str4)) != null || (flag && (num & 32) == 0 && (ptr2 = Number.MatchChars(ptr, str2)) != null)))
{
num |= 16;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if ((options & NumberStyles.AllowThousands) == NumberStyles.None || (num & 4) == 0 || (num & 16) != 0 || ((ptr2 = Number.MatchChars(ptr, str5)) == null && (!flag || (num & 32) != 0 || (ptr2 = Number.MatchChars(ptr, str3)) == null)))
{
break;
}
ptr = ptr2 - (IntPtr)2 / 2;
}
}
c = *(ptr += (IntPtr)2 / 2);
}
bool flag3 = false;
number.precision = num3;
number.digits[(IntPtr)num3] = '\0';
if ((num & 4) != 0)
{
if ((c == 'E' || c == 'e') && (options & NumberStyles.AllowExponent) != NumberStyles.None)
{
char* ptr3 = ptr;
c = *(ptr += (IntPtr)2 / 2);
char* ptr2;
if ((ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
c = *(ptr = ptr2);
}
else
{
if ((ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
c = *(ptr = ptr2);
flag3 = true;
}
}
if (c >= '0' && c <= '9')
{
int num4 = 0;
do
{
num4 = num4 * 10 + (int)(c - '0');
c = *(ptr += (IntPtr)2 / 2);
if (num4 > 1000)
{
num4 = 9999;
while (c >= '0' && c <= '9')
{
c = *(ptr += (IntPtr)2 / 2);
}
}
}
while (c >= '0' && c <= '9');
if (flag3)
{
num4 = -num4;
}
number.scale += num4;
}
else
{
ptr = ptr3;
c = *ptr;
}
}
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowTrailingWhite) == NumberStyles.None)
{
bool flag2;
char* ptr2;
if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == ')' && (num & 2) != 0)
{
num &= -3;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
if ((num & 2) == 0)
{
if ((num & 8) == 0)
{
if (!parseDecimal)
{
number.scale = 0;
}
if ((num & 16) == 0)
{
number.sign = false;
}
}
str = ptr;
return true;
}
}
str = ptr;
return false;
}

Easier way of checking Int32.TryParse

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

What is better: int.TryParse or try { int.Parse() } catch

Better is highly subjective. For instance, I personally prefer int.TryParse, since I most often don't care why the parsing fails, if it fails. However, int.Parse can (according to the documentation) throw three different exceptions:

  • the input is null
  • the input is not in a valid format
  • the input contains a number that produces an overflow

If you care about why it fails, then int.Parse is clearly the better choice.

As always, context is king.

Using int.TryParse with nullable int

Because the out has to be int you need something like:

int temp;
int? nr1 = int.TryParse(str1, out temp) ? temp : default(int?);

Note that I also use default(int?) instead of null because the conditional typing won't work otherwise. ? (int?)temp : null or ? temp : (int?)null would also solve that.

As of C#7 (VS Studio 2017) you can inline the declaration of temp

int? nr1 = int.TryParse(str1, out int temp) ? temp : default(int?);

How can i handle if int.TryParse(string, out int) can't fit the int, ie. they enter a really big number

Use Int32.Parse in a try-catch statement:

try {
Int32.Parse(string);
} catch (System.OverflowException e) {
// do stuff
}
// be sure to catch all other possible exceptions here

https://msdn.microsoft.com/en-us/library/b3h1hf19(v=vs.110).aspx

The TryParse method already handles this kind of stuff internally, and doesn't allow you to catch exceptions manually. The Parse method can fail and will throw an exception, so you'll have to catch all other possible exceptions. See the above link for all other exceptions the method might throw.

The OverflowException occurs when the entered value is lower than Int32.MinValue, or higher than Int32.MaxValue.

int.TryParse syntatic sugar

int intValue = int.TryParse(stringValue, out intValue) ? intValue : 0;

Why is TryParse the way round that it is?

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.

Whoa, what the TryParse

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.



Related Topics



Leave a reply



Submit