Valid and invalid data
-
Depending on the purpose of your program, each variable might have a limited range of values that are “valid” or “good,” even if the data type can hold more
-
For example, a
decimal
variable that holds a price (in dollars) should have a positive value, even though it is legal to store negative numbers in adecimal
-
Consider the
Item
class, which represents an item sold in a store. It has aprice
attribute that should only store positive values:
- When you write a program that constructs an
Item
from literal values, you (the programmer) can make sure you only use positive prices. However, if you construct anItem
based on input provided by the user, you cannot be certain that the user will follow directions and enter a valid price:
In this code, if the user enters a negative number, the myItem
object
will have a negative price, even though that does not make sense.
- One way to guard against “bad” user input values is to use an
if
statement or a conditional operator, as we saw in the previous lecture (Switch and Conditional), to provide a default value if the user’s input is invalid. In our example with Item, we could add a conditional operator to check whetherprice
is negative before providing it to theItem
constructor:
In this code, the second argument to the Item
constructor is the
result of the conditional operator, which will be 0 if price
is
negative.
- You can also put the conditional operator inside the constructor, to
ensure that an
Item
with an invalid price can never be created. If we wrote this constructor inside theItem
class:
then the instantiation new Item(desc, price)
would never be able to
create an object with a negative price. If the user provides an invalid
price, the constructor will ignore their value and initialize the
price
instance variable to 0 instead.
Ensuring data is valid with a loop
-
Another way to protect your program from “bad” user input is to check whether the data is valid as soon as the user enters it, and prompt him/her to re-enter the data if it is not valid
-
A
while
loop is the perfect fit for this approach: you can write a loop condition that is true when the user’s input is invalid, and ask the user for input in the body of the loop. This means your program will repeatedly ask the user for input until he/she enters valid data. -
This code uses a
while
loop to ensure the user enters a non-negative price:
- The condition for the
while
loop isprice < 0
, which is true when the user’s input is invalid - If the user enters a valid price the first time, the loop will not
execute at all — remember that a
while
loop will skip the code block if the condition is false - Inside the loop’s body, we ask the user for input again, and assign
the result of
decimal.Parse
to the sameprice
variable we use in the loop condition. This is what ensures that the loop will end: the variable in the condition gets changed in the body. - If the user still enters a negative price, the loop condition will be true, and the body will execute again (prompting them to try again)
- If the user enters a valid price, the loop condition will be false, so the program will proceed to the next line and instantiate the Item
- Note that the only way for the program to “escape” from the
while
loop is for the user to enter a valid price. This means thatnew Item(desc, price)
is guaranteed to create an Item with a non-negative price, even if we did not write the constructor that checks whetherinitPrice >= 0
. On the next line of code after the end of awhile
loop, you can be certain that the loop’s condition is false, otherwise execution would not have reached that point.
Ensuring the user enters a number with TryParse
-
Another way that user input might be invalid: When asked for a number, the user could enter something that is not a number
-
The
Parse
methods we have been using assume that thestring
they are given (in the argument) is a valid number, and produce a run-time error if it is not -
For example, this program will crash if the user enters “hello” instead of a number:
-
Each built-in data type has a TryParse method that will attempt to convert a
string
to a number, but will not crash (produce a run-time error) if the conversion fails. Instead, TryParse indicates failure by returning the Boolean valuefalse
-
The
TryParse
method is used like this:
-
The first parameter is a
string
to be parsed (userInput
) -
The second parameter is an out parameter, and it is the name of a variable that will be assigned the result of the conversion. The keyword
out
indicates that a method parameter is used for output rather than input, and so the variable you use for that argument will be changed by the method. -
The return type of
TryParse
isbool
, notint
, and the value returned indicates whether the input string was successfully parsed -
If the string
userInput
contains an integer,TryParse
will assign that integer value tointVar
and returntrue
(which gets assigned tosuccess
) -
If the string
userInput
does not contain an integer,TryParse
will assign 0 tointVar
and returnfalse
(which gets assigned tosuccess
) -
Either way, the program will not crash, and
intVar
will be assigned a new value -
The other data types have
TryParse
methods that are used the same way. The code will follow this general format:
Note that the variable you use in the out parameter must be the same
type as the one whose TryParse
method is being called. If you write
decimal.TryParse
, the out parameter must be a decimal
variable.
- A more complete example of using
TryParse
:
-
The
TryParse
method will attempt to convert the user’s input to anint
and store the result inintVar
-
If the user entered an integer,
success
will betrue
, and the program will display “The value entered was an integer:” followed by the user’s value -
If the user entered some other string,
success
will befalse
, and the program will display a message indicating that it was not an integer -
Either way,
intVar
will be assigned a value, so it is safe to writeConsole.WriteLine(intVar)
. This will display the user’s input if the user entered an integer, or “0” if the user did not enter an integer. -
Just like with
Parse
, you can useConsole.ReadLine()
itself as the first argument rather than astring
variable. Also, you can declare the output variable within the out parameter, instead of on a previous line. So we can read user input, declare anint
variable, and attempt to parse the user’s input all on one line:
- You can use the return value of
TryParse
in awhile
loop to keep prompting the user until they enter valid input:
-
The loop condition should be true when the user’s input is invalid, so we use the negation operator
!
to write a condition that is true whensuccess
isfalse
-
Each time the loop body executes, both
success
andnumber
are assigned new values byTryParse