0

I am following a c# course and trying to upgrade my user input method to check if the entered console input is an integer. I have written myself into a do while loop that I know doesn't work, but I am struggling a bit with coming up with a method that can both check for value and if the entered variable is an integer.

So what I tried here was to do-while until the user entered input is an Integer and between the min and max values. But I get stuck on result is only set to a value in the 'if' block, not the 'else' block. It won't compile unless result is set to something, unassigned variable. And I understand why, because there is a branch where I end up with a variable without value, and that won't pass in my while greater-less comparisson. You can only compare numbers, not nulls or strings.

Should I abandon the do-while loop for something more clever? Right now my 'hack' is to set result = 0 in the case that TryParse is false. That is only useful as long as the user does not need to input 0, in which case the whole thing makes no sense any longer.

 static int readInt(string prompt, int low, int high) // METHOD readInt 
    {
        int result;
        bool succes;
        do
        {
            int temp;
            string intString = readString(prompt); 
            succes = Int32.TryParse(intString, out temp);
            if (succes)
            { Console.WriteLine("The string was a number within the limits, {0}.", intString);
                result = int.Parse(intString);
            }
            else
            {
                    Console.WriteLine("{0} is not a valid number between {1} and {2}", intString, low, high);
                result = 0;
            }

        } while (!succes && (result < low) || (result > high)); 
        return result;
    }
sBirch
  • 3
  • 2
  • You need to assign your local variables before using them. – SᴇM Mar 12 '19 at 08:56
  • You don't need to parse it twice. Change `succes = Int32.TryParse(intString, out temp);` to `succes = Int32.TryParse(intString, out result);` – Matthew Watson Mar 12 '19 at 08:56
  • So reason it through - you enter the `else` block if `succes` (sic) is false. And your logic in the `while` specifically goes on to check `result` in that case. `temp` is a perfectly good parsed `int` value - why are you throwing it away and doing a second parse? – Damien_The_Unbeliever Mar 12 '19 at 08:56
  • 1
    Possible duplicate of [What does "Use of unassigned local variable" mean?](https://stackoverflow.com/questions/5710485/what-does-use-of-unassigned-local-variable-mean) – SᴇM Mar 12 '19 at 08:57
  • Your while statement should read `!succes || result < low || result > high` since you want to loop while either of the three conditions is true. – Hans Kilian Mar 12 '19 at 08:58
  • @SeM I am aware of the local variable, it is what gives me a problem since if I do not set it, the if/else block will not compile. So I set the int result = 0. But now I have put something in the place of what was supposed to be a user input. It should preferably not be 0 or any other integer, until a user has entered a valid integer within a specified limit (which is tested first by TryParse, and then by the while bit where I check for greater than or less than. – sBirch Mar 12 '19 at 13:21
  • @HansKilian yes I can try that change. But I was concerned that you cannot do a greater than/less than compare to a string. Which is why I added the TryParse to make sure that result is an int. So if the bool succes is false, the greater/lesser comparisson will not work. So everything depends on the bool being true. – sBirch Mar 12 '19 at 13:24
  • @sBirch you can initialize your `result` with `null` by using [nullable integers](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/): `int? result = null;`, so if your `Parse()` fails (and I suggest you to use `TryParse` instead), it will remain `null`, which user cannot input. – SᴇM Mar 12 '19 at 13:31
  • @SeM ah thank you, I was not aware that I could use nullable integers. I shall read up on that, because I would indeed prefer to have my variable not set to any value yet, unless the user has made an input. – sBirch Mar 13 '19 at 07:24

6 Answers6

0

you can change your " result = int.Parse(intString);" in the "if" with "return temp";

you already have the number from your TryParse, so you do not need to parse it again; returning inside the "if" also removes the need to assign a value to 'result' inside the "else" in fact, you don't need "result" at all)

static int readInt(string prompt, int low, int high) // METHOD readInt 
{
    bool succes;
    do
    {
        string intString = readString(prompt); 
        succes = Int32.TryParse(intString, out int temp);
        if (succes)
        { Console.WriteLine("The string was a number within the limits, {0}.", intString);
            return temp;
        }
        else
        {
                Console.WriteLine("{0} is not a valid number between {1} and {2}", intString, low, high);
        }

    } while (!succes && (result < low) || (result > high)); 
}
ThisIsMe
  • 274
  • 1
  • 5
0

It might be easier to just use a while (true) loop and return from inside the loop when you have a valid result. This is a structured construct known as a loop with one exit, so it's fine to use (if you're worried about structured programming).

For example:

static int readInt(string prompt, int low, int high) // METHOD readInt 
{
    while (true)
    {
        string intString = readString(prompt);

        if (Int32.TryParse(intString, out var result) && (result >= low) && (result <= high))
        {
            Console.WriteLine("The string was a number within the limits, {0}.", intString);
            return result;
        }
        else
        {
            Console.WriteLine("{0} is not a valid number between {1} and {2}", intString, low, high);
        }
    }
}

Note the use of a relatively recent C# feature, the ability to declare an out variable at the point of use using the var keyword - see the out var result inside the TryParse().

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Thanks, @matthew-watson. I am using the out statement in my method. And this while (true) loop is exactly the logic that I struggled to figure out. I was not aware that you could/should declare variables with var. It is not something done in the textbook that I follow. But I now declare the out variable in the call as "out int result" and it works as I expect now. Again, thanks. – sBirch Mar 13 '19 at 12:15
0

Or for a simpler pattern

int result;
string intString;

while (!int.TryParse(intString = Console.ReadLine(), out result) || result < low || result > high)
   Console.WriteLine($"{intString} is not a valid number between {low} and {high}");

Console.WriteLine("The string was a number within the limits, {result}.");

return result;
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
0

You had a couple of bugs in the code. This works:

    static int readInt(int low, int high)
    {
        int result;
        bool success;
        bool outOfLimits = false;
        do
        {
            Console.Write("Enter a number: ");
            string intString = Console.ReadLine();
            success = Int32.TryParse(intString, out result);
            if (!success)
            {
                Console.WriteLine("{0} is not a valid number.", intString, low, high);
                continue;
            }
            outOfLimits = result < low || result > high;
            if (outOfLimits)
                Console.WriteLine("The string was NOT a number between {1} and {2}.", intString, low, high);
            else
                Console.WriteLine("The string was a number within the limits, {0}.", intString);

        } while (!success || outOfLimits);
        return result;
    }
elgaspar
  • 458
  • 3
  • 11
0

Try implementing the routine validating the conditions one after one (we have no need to make code too complex with !succes && (result < low) || (result > high) check):

  1. If user input a valid integer (int.TryParse)?
  2. If valid is it within the range?

If any validation fails keep asking user:

static int readInt(string prompt, int low, int high) 
{
    // Keep on looping until we return a valid result
    while (true) 
    {
        // Or 
        // Console.WriteLine(prompt);  
        // string input = Console.ReadLine();
        string input = readString(prompt); 

        int result = 0; // initialization: let the compiler be happy

        if (!int.TryParse(input, out result)) // Do we have an syntactically invalid input?
          Console.WriteLine($"{input} is not a valid integer number");
        else if (result < low || result > high) // If result is valid; is it out of range? 
          Console.WriteLine($"{input} is out of [{low}..{high}] range");
        else // result is valid integer and within the ranges: time to return it
          return result; 
    }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Many thanks Dmitry, I see the logic way out of it and have simplified a complex value comparisson. – sBirch Mar 13 '19 at 07:23
  • @sBirch: You are welcome! We often call such usage of `if .. else if ... else` as a contract; it's easy to add more conditions to the contract: if you, say, accept odd prime numbers only all you have to do is to add `else if (result % 2 == 0) ... else if (!IsPrime(result)) ...` or remove (comment out) existing ones. – Dmitry Bychenko Mar 13 '19 at 07:42
0

When code evolves like yours, it sometimes gets a bit unclear what it does as the complexity rises. Usually that's a sign that you should refactor it.

I would try to make the code clearer by moving all the checks for validity into a method by itself. Then you can do something like this.

   static int readInt(string prompt, int low, int high) // METHOD readInt 
    {
        bool valid = false;
        int result = 0;
        while (!valid)
        {
            var intString = readString(prompt);
            valid = checkIfValid(intString, low, high, out result);
        }
        return result;
    }

    static bool checkIfValid(string s, int low, int high, out int result)
    {
        if (!Int32.TryParse(s, out result))
        {
            Console.WriteLine(s + " isn't an integer");
            return false;
        }

        if (result < low)
        {
            Console.WriteLine("Number is too low");
            return false;
        }

        if (result > high)
        {
            Console.WriteLine("Number is too high");
            return false;
        }

        return true;
    }
Hans Kilian
  • 18,948
  • 1
  • 26
  • 35
  • Yes @Hans Kilian, I was thinking this myself too, the integer checking could be its own method that I could call in other circumstances. But I do not yet have the skill to come up with that what you propose :) Also, I would prefer the result to be null or not set, if I set it to 0 I sort of already did the user input. What if user enters 0? So I need to make something that will work even if int result is a null, so I can see when the user did input a value. – sBirch Mar 13 '19 at 12:24