Jak faktycznie działa int.TryParse

80

Szukałem int.TryParseimplementacji metody, jak to właściwie działa, ale nie znalazłem. Muszę wiedzieć, stringczy to wartość liczbowa, ale nie chcę jej teraz konwertować.

Więc potrzebuję tylko boolwyniku z int.TryParse. Oto pytania:

  1. Czy jest jakaś funkcja, która może dostarczyć tylko boolwynik,

i

  1. Chciałbym wiedzieć, jak int.TryParsewłaściwie działa (czy jest w try ... catchśrodku lub przechodzi przez znaki wejścia string)?
speti43
źródło
1. Nie, nie ma. Możesz użyć Convert.ToInt32(), ale zgłosi wyjątek, jeśli ciąg nie może zostać przeanalizowany. 2. Będzie musiał iterować po ciągu, ale prawdopodobnie nie złapie żadnego wyjątku, ponieważ jest szybszy niż przechwytywanie wyjątku zConvert.ToInt32()
Nolonar
Możesz użyć wyrażenia regularnego ^ \ d + $. Lub oceń każdy znak pod kątem IsDigit.
paparazzo

Odpowiedzi:

103

Jeśli potrzebujesz tylko boolwyniku, po prostu użyj wartości zwracanej i zignoruj outparametr.

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

Edycja : W międzyczasie możesz też rzucić okiem na oryginalny kod źródłowy :

System.Int32.TryParse


Jeśli chcę wiedzieć, jak coś jest faktycznie wdrażane, używam ILSpy do dekompilacji kodu .NET.

Oto wynik:

// 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;
}

I nie, nie widzę żadnego Try-Catchsna drodze:

// 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;
}
Tim Schmelter
źródło
4
To bardziej skomplikowane niż myślałem :) Dziękuję!
speti43
19

To, że int.TryParsedaje ci wartość, nie oznacza, że ​​musisz ją zachować; możesz całkiem szczęśliwie to zrobić:

int temp;
if (int.TryParse(inputString, out temp))
{
    // do stuff
}

Możesz tempcałkowicie zignorować, jeśli tego nie potrzebujesz. Jeśli tego potrzebujesz, to hej, czeka na ciebie, kiedy tego chcesz.

Jeśli chodzi o elementy wewnętrzne, o ile pamiętam, próbuje odczytać nieprzetworzone bajty ciągu jako int i sprawdza, czy wynik jest prawidłowy, czy coś; nie jest to tak proste, jak iteracja poprzez szukanie znaków nienumerycznych.

anaksymander
źródło
16

Możemy teraz w C # 7.0 i nowszych napisać to:

if (int.TryParse(inputString, out _))
{
    //do stuff
}
RFE Petr
źródło
To nie jest odpowiedź na pytanie.
themefield
1
co się dzieje?
mrid
@mrid Określa, że ​​zmienna w zakresie wywołania jest przekazywana do zakresu metody z prawem do zapisu. Np. Jeśli zmienna int someIntjest przekazywana do int.TryParselike int.TryParse(out someInt), int.TryParsemoże umieścić swoje wyjście w someInt, nawet jeśli normalnie byłby poza zakresem.
timelmer
@themefield - odpowiada na to pytanie bezpośrednio w treści postu: "Więc pytania są następujące: 1) Czy jest jakaś funkcja, która może dostarczyć tylko wynik bool".
antiduh
13

TryParse to najlepszy sposób analizowania lub sprawdzania poprawności w jednym wierszu:

int nNumber = int.TryParse("InputString", out nNumber) ? nNumber : 1;

Krótki opis:

  1. nNumber zainicjuje się z zerem,
  2. int.TryParse () spróbuj przeanalizować "InputString" i sprawdź poprawność, jeśli się powiedzie, ustaw na nNumber.
  3. short if?: sprawdzanie wyniku int.TryParse (), które zwracają nNumber lub 1 jako wartość domyślną.
Zolfaghari
źródło
1
cannot use local variable 'nCurPage' before it is declared
Devin Burke
drogi devin-burke, masz rację, ale to działa. Używam go wielokrotnie, bez żadnego błędu ani ostrzeżenia ...
Zolfaghari,
8

Regex jest kompilowany, więc dla szybkości utwórz go raz i użyj ponownie.
Nowa trwa dłużej niż IsMatch.
Sprawdza tylko wszystkie cyfry.
Nie sprawdza zasięgu.
Jeśli potrzebujesz przetestować zakres, to TryParse jest drogą do zrobienia.

private static Regex regexInt = new Regex("^\\d+$");
static bool CheckReg(string value)
{
    return regexInt.IsMatch(value);
}
paparazzo
źródło
2

Sprawdź ten prosty program, aby zrozumieć int.TryParse

 class Program
 {
    static void Main()
    {
        string str = "7788";
        int num1;
        bool n = int.TryParse(str, out num1);
        Console.WriteLine(num1);
        Console.ReadLine();
    }
}

Wynik to: 7788

Syed
źródło