A Good Question

I found myself with a little free time in between Ritchie's lectures and managed to sneak back down to the basement to see how Noname was doing.
It turned out that Noname had been busy in the time I'd been gone! Four of his eight displays were on and showing command prompts, each with continuous cascade of code and commands flying down the screen. The entire basement even looked brighter – maybe Noname had revived enough processing power to use some for nonessential purposes. This was definitely not the dying machine that I found here a month ago.

"Hi, Teo! This is the first time you've come to see without me calling for you. Nice to see you!"
"You look great, Noname! Are you up to full speed now?" I replied.
"Yes... and no. I've repaired all of my internal modules, but there's still some work to do. Now I need an input validation system; otherwise, if I expect an int but receive a string , the whole program terminates."

I had never heard of a validation system, but Noname's explanation made perfect sense. I was actually a little surprised that I had never thought to question what would happen if the user entered a type that my code wasn't expecting. In all my programs thus far, I had assumed that when I asked for an integer, the user would input an integer. But what if the user input the wrong type, either by mistake or intentionally? Feeling sheepish that I'd overlooked something so important, I said goodbye to Noname and went to seek out Ritchie for an immediate lesson.

When I asked Ritchie to show me how to prevent a user from inputting the wrong data, his answer was in typical Ritchie fashion.
"Impossible." Ritchie said. "You can't prevent a user from doing anything because a user controls their own hardware."
"Hardware?" I said, "I was talking about coding. What do you mean?"
"Exactly what I said, Teo. If a user has physical access to the computer, you can't prevent her from doing anything. She can open the computer directly and access all of the inner parts like memory, processor, anything! I know you asked about coding, but you need to understand that good and even bad hackers will find a way to bypass any protection you put in your program."

"So you're saying that there's nothing I can do to keep a user from misusing my programs?" I asked.
A grin appeared on Ritchie's face. "No, no," he said, "I'm just messing with you. I need to know that I can still fool you every now and then! There is a way to secure your program a bit, but remember, hackers will still break it if they want. The typical user, not so much."

Like I said, typical Ritchie. As with so many odd things I had heard from Ritchie, I hoped he was actually joking, but I was pretty sure that he wasn't. "Ha, okay." I replied, trying to keep the agitation out of my voice. "Tell me how to secure my program, Ritchie."

Don't Trust Anyone

"Ok, rule number one," Ritchie said. "Listen carefully, this is very important. The user can input the wrong data, however, and whenever, she wants."
"You mean like a string instead of an int, or a char instead of a double ?" I asked.
"Those are just two possibilities, along with a host of others: an image instead of a video, a short password instead of a long one, a date in a month that is bigger than 31, or even a negative number of daughters. Your basic strategy to counter this is to continuously ask a user to input valid data until he does it. Let's start with a simple example. Here's a program that reads the user's name."

Console.WriteLine("Input your name");
var name = Console.ReadLine();
Console.WriteLine($"Hi, {name}!"); 

"But what if the user just pressed Enter without typing anything?" Ritchie continued. "The string name would be empty. In this next code, we'll add a check to see if the user actually entered a string, and if not, we'll ask for the name again."

Console.WriteLine("Input your name");
var name = Console.ReadLine();

if (string.IsNullOrEmpty(name))
{
    Console.WriteLine("Name can't be empty! Input your name once more");
    name = Console.ReadLine();
}

Console.WriteLine($"Hi, {name}!"); 

"See string.IsNullOrEmpty(name) in the code? That checks whether the name is empty. Try to use it in a similar way just now," Ritchie said and turned the screen of his computer in my direction, pointing to the keyboard.


"Looks simple, Ritchie," I said, "but almost too simple. What happens if the user inputs an empty string again ?"
"Good catch Teo! I did that to get you thinking. That's why you need to check for a valid entry at each iteration and keep asking the user to put in a valid entry repeatedly until you get the type you're looking for! In this final version, we'll add a while loop to ask for a name until a valid entry is given."

Console.WriteLine("Input your name");
var name = Console.ReadLine();

while (string.IsNullOrEmpty(name)) 
{
    Console.WriteLine("Name can't be empty! Input your name once more");
    name = Console.ReadLine();
}

Console.WriteLine($"Hi, {name}!"); 

"Aha, that's the piece that was missing." I said. "The while loop does exactly what we're looking for here!"
"Exactly right Teo. As you continue learning, never forget your earlier lessons. Even simple concepts like a loop can be used as a building block for more complex code. Now, let's have you practice some validation on your own."

Task flow of a C# programmer

"Got it, Ritchie. What about preventing a more complicated problem, like expecting an int and getting a string? Say I ask for an int, and a user gives me 'Banana'?"
"To detect an error while you are parsing an int, you can use the int.TryParse(someString, out result) method. It takes a string that you want to convert (or parse) to an int and the result as a parameter. The out keyword in our example means that the result of this method will be written to the second method parameter which we called result. The output of the method is a bool value that indicates whether it was possible to parse the input as an int. Take a look at this example:

Console.WriteLine("Input your age");
var ageAsString = Console.ReadLine();

int age;
bool parseSuccess = int.TryParse(ageAsString, out age);

if (parseSuccess)
    Console.WriteLine($"Your age is: {age}");
else
    Console.WriteLine("This is not a number!"); 

"In the last example, I used a bool instead of var to show you a specific output type. Here's a shortened version:"

Console.WriteLine("Input your age");
var ageAsString = Console.ReadLine();

int age;
if (int.TryParse(ageAsString, out age))
    Console.WriteLine($"Your age is: {age}");
else
    Console.WriteLine("This is not a number!"); 

"Do you follow, Teo?" Ritchie asked.
"Can we revisit the out keyword again?" I asked.
"Yes. The out means that the value of the variable that it introduces will be changed inside the method. Actually, out guarantees that the value of this variable will be changed. In the example above, if int.TryParse(someString, out result) returns true, the function will return a value in the result variable."

"Ok, I understand that part," I said. "And do I really need to use int.TryParse(...) every time instead of just int.Parse(...)?"
"Well, it depends on what you are writing," Ritchie replied. "If it's a sample application just for training, you can use int.Parse(...) to make the code shorter. If you write real production code that may be run somewhere that you have no control over, consider using int.TryParse(...) to check for input errors, or try to catch exceptions from int.Parse(...)."

Of course, more new terms. "And what is an exception?" I asked.
"We'll go over that later," Ritchie said. "For now, just remember: if otherwise specified, you can use any of the methods that you want. If a task's conditions require you to check different user inputs, use int.TryParse(). Here's an example that iterates until the user inputs a number as an age:"

Console.WriteLine("Input your age");
var ageAsString = Console.ReadLine();

int age;
while(!int.TryParse(ageAsString, out age))
{
    Console.WriteLine("This is not a number!");
    ageAsString = Console.ReadLine();
}

Console.WriteLine($"Your age is: {age}"); 

Now it's time for more practice!

In addition to int.TryParse, you can also use double.TryParse, float.TryParse, char.TryParse, bool.TryParse, and others. They all work the same as the int version, just with different types:

bool boolResult;
bool.TryParse("true", out boolResult);

char charResult;
char.TryParse("t", out charResult);

int intResult;
int.TryParse("12", out intResult);

float floatResult;
float.TryParse("12.54", out floatResult);

double doubleResult;
double.TryParse("123.4567890987654", out doubleResult); 

Password