Ian Carpenter
Ian Carpenter

Reputation: 8626

Retrying until the user input is correct

I'm learning C# via Rob Miles Yellow Book and have hit a problem with understanding variable scope.

The class I have developed (at the bottom) asks for a number and then displays it back to user. I would like to enhance it so that if the user enters ten instead of 10 it outputs a message and the user has another attempt.

Here is one of several attempts so far at the change...

...

    static int readInt(string prompt, int low, int high)
    {
        int result;

        do
        {
            try
            {
                string intString = readString(prompt);
                result = int.Parse(intString);
                Console.WriteLine("Thank you");
            }
            catch
            {
                Console.WriteLine("Invalid age value");
            }

        } while ((result < low) || (result > high));

        return result;
    }
...

Unfortunately this has the effect (I think) of pushing result out of scope because with this change I see the following compliation error:

CS0165 Use of unassigned local variable 'result'

What is the C# way to achieve this?

The complete class:

using System;

class Exception1
{
    static string readString(string prompt)
    {
        string result;

        do
        {
            Console.Write(prompt);
            result = Console.ReadLine();
        } while (result == "");

        return result;
    }

    static int readInt(string prompt, int low, int high)
    {
        int result;

        do
        {            
            string intString = readString(prompt);
            result = int.Parse(intString);            
        } while ((result < low) || (result > high));

        return result;
    }


    public static void Main()
    {
        const int SCORE_SIZE = 1;

        int[] scores = new int[SCORE_SIZE];

        for (int i = 0; i < SCORE_SIZE; i++)
        {
            scores[i] = readInt("Score : ", 0, 1000);

        }

        for (int i = 0; i < SCORE_SIZE; i++)
        {
            Console.WriteLine("Score " + i + " is " + scores[i]);
        }

        Console.Read();
    }
}    

Upvotes: 2

Views: 1525

Answers (4)

msmolcic
msmolcic

Reputation: 6567

Just wanted to add possible solution if you really don't want to set result to any value initially:

static int readInt(string prompt, int low, int high)
{            
    bool validInput;
    int result;

    do
    {
        if ((validInput = int.TryParse(readString(prompt), out result)))
            Console.WriteLine("Thank you");
        else
            Console.WriteLine("Invalid age value");

    } while (!validInput || (result < low) || (result > high));
}

If TryParse fails, result will be set to integer default value (0) and validInput variable will be set to false so loop will go through once again.

Upvotes: 2

Dan Field
Dan Field

Reputation: 21661

In addition to Patrick Hoffman's answer, which is correct, you should look into using int.TryParse instead of int.Parse.

Using exception handling for flow control is generally a bad idea, and this is illustrative of one reason why. The compiler is recognizing that an exception could happen before result is actually assigned (which is in fact the case if the user never enters a numeric value). It won't be able to evaluate the unassigend variable properly. If you use TryParse, you won't have this particular issue.

Exception handling should only be used when you realize there are factors beyond your control or ability to handle well with regular code and checks (if (bTryParseReturn == false) ....) - when user input might be so problematic that there's no straightforward way to test it, for example. Even then, the exception should generate an error, not just a "try again" situation - it's entirely possible that the exception can never really be recovered from or avoided, especially since you're catching all exceptions (and not just an InvalidCastException or something like that). For example, imagine that the exception is a System.OutOfMemoryException (the user tried to enter a terrabyte of data at your prompt). Your program will try to chug along merrily and horribly crash from the user's perspective.

Upvotes: 1

AndyT
AndyT

Reputation: 176

The compiler can't guarantee the property result is ever set yet still is expected to output result at the end of the method.

There could be an exception thrown or various other issues that prevent result being set prior to reaching the last line of the method.

So the compiler is pre-empting a NullReferenceException scenario and regards it as a compilation error.

It's doing you a favour :)

As previously stated, give the property result a default value to solve this issue.

If the property was at class level it would be implicitly set it's property type default value - but that's for another question.

Upvotes: 1

Patrick Hofman
Patrick Hofman

Reputation: 157048

The compiler doesn't know that the while loop actually runs (it can throw and exception before assigning result) and sets a value on result. Therefore it complaints about this.

You can circumvent this by assigning a default value to result:

int result = -1;

Or by setting a value in the catch block:

catch
{
    result = -1;
    Console.WriteLine("Invalid age value");
}

I always use -1 in these cases as indicator of 'something wrong with this value'.

Upvotes: 6

Related Questions