I want to learn how to implement the int.tryparse command in my code to prevent the program from crashing with invalid input. This is what I've written so far:
using System;
namespace barn_
{
class Program
{
static void Main(string[] args)
{
int age;
string a;
do
{
Console.WriteLine("How old is the person?");
age = Convert.ToInt32(Console.ReadLine());
a = Convert.ToString(Console.ReadLine());
bool v = int.TryParse(a, out age);
while(a == true)
{
Console.WriteLine("");
Console.WriteLine("");
}
if (age >= 2 && age <= 5)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("The person has blue clothes.");
}
else if (age >= 6 && age <= 9 || age >= 10 && age <= 15)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The person has red clothes.");
}
else if (age < 2)
{
Console.WriteLine("The person is too young.");
Console.WriteLine();
}
else if (age > 15)
{
Console.WriteLine("The person is too old.");
Console.WriteLine();
}
} while (age < 2 || age > 15);
}
}
}
CodePudding user response:
You can extract a method to read integer values:
private static int ReadInteger(string title, int from, int to) {
// Keep asking user until some valid input is provided
while (true) {
// If we have a title, print it
if (!string.IsNullOrWhiteSpace(title))
Console.WriteLine(title);
// First we try to parse user input
if (int.TryParse(Console.ReadLine(), out int result))
// if parsing succeeds, check for ranges
if (result >= from && result <= to)
return result; // result is a valid value in [from..to] range
else // valid integer, but out of [from..to] range, say, 12345 or -97
Console.WriteLine($"Value is out of [{from}..{to}] range. Please, try again.");
else // not a valid integer, say, "bla-bla-bla"
Console.WriteLine("Not a valid integer value. Please, try again.");
}
}
Then you can use this method as follow:
static void Main(string[] args) {
// we read and integer, say, in [0..122] range
// https://en.wikipedia.org/wiki/Jeanne_Calment
int age = ReadInteger("How old is the person?", 0, 122);
// try to order the conditions: when ordered they are more readable
if (age < 2)
Console.WriteLine("The person is too young.");
else if (age <= 5) {
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("The person has blue clothes.");
}
else if (age <= 15) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The person has red clothes.");
}
else
Console.WriteLine("The person is too old.");
}
CodePudding user response:
With my following answer, I will try to avoid possible bad habits. My code example is probably exaggerated for most use cases, but I would like to clarify a few points and explain the aspects of reusability in object-oriented programming. However, @Dmitry Bychenko's answer and the comments to your question solve your described problem with a high degree of certainty.
Depending on which IDE you are developing in (e.g. Visual Studio 2019), well-documented methods, which is the case for most system methods such as int.TryParse
or Convert.ToInt32
, will show you which exceptions can occur.
For example, with int.TryParse
no exception can be thrown if no integer is entered. This is different with Convert.ToInt32
. If you try to convert a string that is not an integer with Convert.ToInt32
, the exception FormatException
is thrown, indicating that the part to be converted is not an integer.
Now to the code example and reusability. The following two code snippets of your example are very similar. This can mostly be deduced from the fact that a method can be generated out of them. In Visual Studio 2019, automatically generated method can be created with "Ctrl R M".
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("The person has blue clothes.");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The person has red clothes.");
In later code example this method is found with the name "PrintTextInConsoleInColor". The good reason for using a method is that the colour is set and a text is printed. This pattern repeats itself. If you want to output a sound every time the colour is set, you have to change EVERY part where the code occurs. This is very bad and involves a lot of effort and even inconsistency. This is very serious and time consuming for larger projects, which is of course the case for more complex processes than setting a colour and playing a sound, which is just for demo.
The naming of methods also plays an important role. One has to think that it is not only console input. That's why the name "GetIntegerFromConsoleInput" is exaggerated in the context of a pure console application, although it should always be clear from the name of a method what is happening inside or when invoking it, so that you are not suddenly surprised when it is executed.
In terms of reusability, it makes sense to split the logic you want into several parts, such as "GetIntegerFromConsoleInput" and "GetIntegerInRangeFromConsoleInput". The reason for this is that if you need ONLY one number, the method "GetIntegerFromConsoleInput" can be used. However, if you require a number within a range, as in your case, "GetIntegerInRangeFromConsoleInput" will be used. If you now look at the documentation of the method, you can see the documentation of the exception, which is also applied by an IDE like "Visual Studio". Basically, it is also recommended to avoid nesting "if-statements" and "loops", as this strongly affects the readability of the code. In the Console, an empty line can be inserted by a control character, which saves you the multiple use of "Console.WriteLine("")".
Console.WriteLine("Hello\nWorld");
Finally, a potential solution for writing a class for ConsoleIO and how it can be reused.
public class ConsoleIOHelper
{
/// <summary>
/// Get console input within range.
/// </summary>
/// <param name="lowerBoundary"> Lower boundary included in Range.</param>
/// <param name="upperBoundary"> Upper boundary included in Range.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown in case the lower boundary is greater than the upper boundary.</exception>
public int GetIntegerInRangeFromConsoleInput(int lowerBoundary, int upperBoundary)
{
if (lowerBoundary >= upperBoundary)
{
throw new ArgumentOutOfRangeException(nameof(lowerBoundary), "Lower boundary has to be lower than upper boundary!");
}
int result;
bool isInRange;
do
{
result = GetIntegerFromConsoleInput();
isInRange = IsInBoundaries(result, lowerBoundary, upperBoundary);
if (!isInRange)
{
PrintTextInConsoleInColor($"Input {result} is not within boundaries of {lowerBoundary} and {upperBoundary}", ConsoleColor.Red);
}
}
while (!isInRange);
return result;
}
/// <summary>
/// Get interger from console input.
/// </summary>
public int GetIntegerFromConsoleInput()
{
bool isInteger;
int result;
do
{
isInteger = int.TryParse(Console.ReadLine(), out result);
if (!isInteger)
{
PrintTextInConsoleInColor("Input is not a number!", ConsoleColor.Red);
}
}
while (!isInteger);
return result;
}
/// <summary>
/// Checks weather a input number is within lower and upper boundary.
/// </summary>
private bool IsInBoundaries(int input, int lowerBoundary, int upperBoundary)
{
return input >= lowerBoundary && input <= upperBoundary;
}
private void PrintTextInConsoleInColor(string message, ConsoleColor consoleColor)
{
Console.ForegroundColor = consoleColor;
Console.WriteLine(message);
Console.ResetColor();
}
}
I hope that I have been able to bring more clarity to your question, even if I have deviated greatly from the actual question. I apologise in case I have described things that were already known, I just wanted to use the example to point out things that struck me.