I have a try {} catch {} for each line my user inputs something, this is to ensure it's in the right format/range/acceptable. However it doesn't seem to... well work! Here is one of my examples.
string userAnswer;
bool errorHandling = true;
while (errorHandling){
try{
userAnswer = Console.ReadLine();
if (userAnswer == "1") {
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
}else{
if (userAnswer == "2"){
readIn.Classes(SingleGrade[1]);
}else{
if (userAnswer == "3"){
countAll.allGrades(multiGrade);
} else{
errorHandling = false;
}
}
}
}
catch (FormatException a){
Console.WriteLine(a.Message);
//Console.WriteLine("Error - please enter a number between 1 & 6.");
}
} // end of While loop
If someone could please explain to me why no error is caught when an invalid number is placed/not in the correct format.
There is no FormatException being thrown because valid strings are being entered. If you were to convert the user input to an integer, for example, that would throw a FormatException. But since you are leaving it as a string, no exception is being thrown.
However, since you are really only trying to restrict user input, and nothing truly exceptional is happening here, you should probably just handle it through you application logic.
What you are really looking for is probably something like this:
bool errorHandling = true;
while (errorHandling)
{
string userAnswer = Console.ReadLine();
switch (userAnswer)
{
case "1":
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
errorHandling = false;
break;
case "2":
readIn.Classes(SingleGrade[1]);
errorHandling = false;
break;
case "3":
countAll.allGrades(multiGrade);
errorHandling = false;
break;
// cases for 4, 5, and 6...
default:
Console.WriteLine("Error - please enter a number between 1 & 6.");
break;
}
} // end of While loop
You're not throwing and exception when the number is invalid, you're just handling a bunch of if statements - thus, since there is no throw statement, nothing can hit the catch statement, and insteadyou're seeing what we colloquially call a "Fall through error" - logic is proceeding past where it should.
If you want an exception when input is invalid, just add a throw new FormatException to the end of the if statements, so you get a "if we make it here there's a problem" behaviour.
The reason is that you are grabbing a string, and not trying to convert it to anything else, so there is no FormatException.
I would get away from using the try / catch for input validation, and instead use the TryParse method of the Int type.
What I usually do is write a separate function to get validated input from the user, then call that from my main code. In this case, since you have upper and lower bound requirements, you might consider writing something like this:
public static int GetBoundedIntFromUser(int minVal, int maxVal)
{
if (minVal >= maxVal)
{
throw new ArgumentException("minVal must be less than maxVal.");
}
int input;
while (true)
{
Console.Write("Please enter a number from {0} to {1}: ", minVal, maxVal);
if (int.TryParse(Console.ReadLine(), out input)
&& input >= minVal && input <= maxVal)
{
break;
}
Console.WriteLine("That input is not valid. Please try again.");
}
return input;
}
Then you can just call it from your main code (without any try catch), like:
switch (GetBoundedIntFromUser(1, 3))
{
case 1:
singleGrade.grapher(acount, bcount, ccount, dcount, ecount, fcount);
break;
case 2:
readIn.Classes(SingleGrade[1]);
break;
case 3:
countAll.allGrades(multiGrade);
break;
default:
break;
}
Related
I'm writing a method that I will use in another code so I'm writing it separately first.
The code returns a boolean based on the first char of the input (yes, Yes, y, yeah, No, ...). But when the input is different from Y or N it starts acting up. In eg3 It stays in the loop until The Console.Read encounters a Y or an N or there are no chars left. In the last case, it will ask again for input.
Am I using the Console.Read wrong or is there another error in my code?
Thanks in advance
EDIT: Is the solution, in the end, an efficient one?
eg1:
Yes or No?
YeahIthinkso <--my input
True
eg2:
Yes or No?
Negative <--my input
False
eg3:
Yes or No?
Idontknow <--my input
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
False
The Code:
static void Main(string[] args)
{
char YN = 'x';
bool ans = false;
while (YN == 'x')
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Convert.ToChar(Console.Read()));
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
Console.WriteLine(ans);
Alternate solution based on #StuartLC's answer:
bool ans = true, loop = true;
do
{
switch (Console.ReadLine().ToUpper().FirstOrDefault())
{
case 'Y':
ans = true;
loop = false;
break;
case 'N':
ans = false;
loop = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
break;
}
} while (loop==true);
Console.WriteLine(ans);
As per #Lasse's comment - if the user types in multiple characters, you'll loop on each character in the string typed in by the user, resulting in the printing a new line of output for each character the user typed. Instead, use of ReadLine will parse the input as a single string, and then the Linq extension FirstOrDefault() will safely obtain the first char in the string:
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
As an aside, instead of starting a while loop with a forced false condition, C# also supports a do-while loop syntax which fits your requirement better, i.e. at least one iteration through the loop, with a check at the end of the loop:
do
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
while (YN == 'x');
Re OP's follow up question
Can I now remove the 'YN' completely and put a switch (Console.ReadLine().FirstOrDefault().ToUpper()) in the beginning and a while (ans == true || ans ==false) in the end?
Not quite - since ans is a boolean, it can only have two states (true or false), and you need to model at least 3 states (true, false, and invalid). Although you could use a nullable boolean (bool?) to model the 3 states (where null = invalid), I personally dislike using null to indicate something isn't known, as this invariably leads to the NullReferenceException abyss, and C# hasn't (yet) opted for the "Option" type / Monad (like Java's Optional).
If you're OK with C#8 pattern matching and tuples, you could possibly make the code a bit more concise and refactored as follows, by splitting out the concerns of 'is the input valid' and 'what is the valid input value'?. You could also refactor the switch expression into it's own method to split out the concerns of 'UI' from the parsing logic, which is always a good idea.
static void Main(string[] args)
{
bool ans;
bool isValid;
do
{
Console.WriteLine("Yes or No?");
(ans, isValid) = ParseInput(Console.ReadLine());
if (!isValid)
{
Console.WriteLine("You can only answer with Yes or No");
}
}
while (!isValid);
Console.WriteLine(ans);
(bool ans, bool isValid) ParseInput(string input) =>
char.ToUpper(input.FirstOrDefault()) switch
{
'Y' => (true, true),
'N' => (false, true),
_ => (default, false)
};
}
I have a method which has result at the end,I would want to detect if number is not 0 and if it's -4.
0 Means good
-4 Means something that can be solve
And anything else is bad.
Like
if ( Result != 0)
{
MessageBox.Show("It's bad!")
}
else if ( Result == -4)
{
Thread.Sleep(20000);
MyMethod.TryAgain();
}
else
{
MessageBox.Show("It's good");
}
My problem is that -4 is not 0,so if i get result -4 it takes my Result != 0 method. How can I solve it? Thank you in advance.
Use switch and case.
switch (Result) {
case 0:
MessageBox.Show("It's good");
break;
case -4:
Thread.Sleep(20000);
MyMethod.TryAgain();
break;
default:
MessageBox.Show("It's bad!");
break;
}
Microsoft documentation: https://msdn.microsoft.com/en-us/library/06tc147t(v=vs.110).aspx
Just reorder your if-structure to the following:
if ( Result == 0)
{
MessageBox.Show("It's good")
}
else if ( Result == -4)
{
Thread.Sleep(20000);
MyMethod.TryAgain();
}
else
{
MessageBox.Show("It's bad");
}
So your initial problem, that the Result != 0 case is evaluated first, is gone.
Simply change order of branches
if (Result == -4) \\ solve
else if (Result != 0) \\ bad
else \\ good
When you are building a chain of non-exclusive conditions, start with the strongest one (i.e. the most specific condition). Otherwise the code for the weaker condition will execute, blocking access to more specific ones.
In your case, Result == -4 implies that Result != 0, meaning that the first condition is more specific than the second one. Hence you should check Result == -4 before Result != 0. Otherwise, Result == -4 would never be reached.
C# offers multiple ways of implementing this logic. As long as you follow a general rule of ordering your conditionals from most specific to least specific, picking a particular implementation is up to you.
Sorry, wrong code. See comment below.
switch(Result) {
case 0:
MessageBox.Show("It's bad!");
break;
case -4:
Thread.Sleep(20000);
MyMethod.TryAgain();
break;
default:
MessageBox.Show("It's good");
break;
}
I am looking to return the bool containsVowel into my main method, where if a word contains a vowel it will allow it to be stored into an array. When I use the code below an error comes up over the method name saying "not all code paths return a value". I've also tried not using the for loop, as well as the char array - it works fine then but if I enter a vowel into the form it still says no vowel has been found.
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
break;
default:
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
break;
}
}
}
Given your method has the return type bool instead of void, all code paths must return a value. The C# compiler tries to very naively evaluate all code paths to see if your method returns a value from all paths.
Your method looks like this to the compiler:
start
for
switch
case
return
default
return
end
The switch is covered alright thanks to the default, but the for is a conditional branch as well. If you pass an empty string, the for won't be entered (i = 0 and word.Length = 0, so i < word.Length is false from the beginning), so after the for, there needs to be a return statement as well.
You can massively improve the code by doing the following:
public bool ContainsVowel(string word)
{
if (string.IsNullOrEmpty(word))
{
return false;
}
var upperCaseCharacters = word.ToUpper().ToCharArray();
foreach (var character in upperCaseCharacters)
{
switch (character)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
}
}
return false;
}
This code checks whether the string being passed is null or empty, and then immediately returns false (fail fast). Then it converts the string to upper case once, and loops over the character array with a foreach(), which is the preferred way to iterate over a collection anyway. Then it doesn't use an intermediate variable to store the result, as you can return true as soon as you encounter a vowel.
Finally, when no vowel has been encountered after iterating over all characters, the method returns false.
Then at the call site, you can do this:
if (!ContainsVowel(word))
{
MessageBox.Show("Word must contain a vowel", "Error");
}
Because methods like this should not contain UI logic such as message boxes.
This code should do.First, check for a null word value.Then you must convert your word to upper in order to detect all vowels, being this upper or lower. Then, you must only check for a vowel, because if one is present the method should return true.If,after checking all the letters, the method has not detected a vowel,show error message and return false :
private static bool vowel(string word)
{
if (word == null) return false;
char[] wordChar = word.ToUpper().ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (word[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
break;
}
}
Console.WriteLine("Word must contain a vowel");
return false;
}
As you can see, this method always returns a value. In your code, if the word passed is empty,it would not enter the forloop, so it won't return anything.
Your code only checked the first character of the string, since it jumped out of the loop if it was vowel or not. This should fix it, but for a better approach, check #Sr82's answer
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
//if you find a vowel, you can return true straight away
}
}
//if you dont find one, let the for loop finish. If it reaches this code you know
//there was no vowel found and you can proceed with your error report
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
}
This simple method should work if you can use Linq. The Intersect method takes two char sequences and returns a sequence of char that exists in both of them. The Any method returns true if there is anything in the sequence.
using System.Linq;
void Main()
{
string s = "Word";
if (hasVowels(s))
{
Console.WriteLine("Word has vowels");
}
else
{
Console.WriteLine("Word does not have vowels");
}
}
//The `Intersect` method takes two `char` sequences and returns a sequence
//of `char` that exists in both of them. The `Any` method returns true if
//there is anything in the resulting sequence.
private bool hasVowels(string word)
{
string vowels = "AEIOUaeiou";
return word != null && word.Intersect(vowels).Any();
}
If you're trying to check whether a string contains a vowel and that's it, you can do something like this:
private bool vowel(string word)
{
return word.ToLower().Any(x => "aeiou".Contains(x));
}
This uses LINQ and specifically the Any method to determine if the string contains any vowels. It would be good to set the string to all lower case so you can check whether a vowel is present irrespective of case.
I am currently working on a program and I am finalising it by going over with error handling. I have several cases which look like:
int stockbankInput = Convert.ToInt32(Console.ReadLine());
Here, the user must enter either 1, 2, 3. I have tried to use an if statement to catch the error if anybody inputs a blankspace/string/character or a number that is not 1,2 or 3 but it doesn't work in the same sense as a string input. Below is what I have tried:
if(stockbankInput == null)
{
Console.WriteLine("Error: Please enter either 1, 2 or 3");
stockbankInput = 0;
goto menuRestartLine;
}
However, you cannot link 'null' with an integer input, only a string. Can anybody help with this please?
Use the Int32 TryParse method:
int input;
var successful = Int32.TryParse(Console.ReadLine(), out input);
if (!successful)
// do something else
else
return input;
You're checking if an int is null, which will always return false because an int cannot be null.
You can use 'int?' (Nullable int) but Convert.ToInt32 will not return null. If the value of the int cannot be resolved it will resolve to the default value of zero. You can either check if the returned int is zero or do some further checking of the returned string:
int input = 0;
string errorMessage = "Error: Please enter either 1, 2 or 3";
while(true)
{
try
{
input = Convert.ToInt32(Console.ReadLine());
if (input == 0 || input > 3)
{
Console.WriteLine(errorMessage);
}
else
{
break;
}
}
catch(FormatException)
{
Console.WriteLine(errorMessage);
}
}
With this you your returned value "int input" will either be 0 or the number you entered and FormatExceptions caused by the string to convert containing symbols other than the digits 0-9 will be caught in the try/catch statement.
give this sample program a try:
static void Main(string[] args)
{
int stockbankInput = 0;
bool firstTry = true;
while(stockbankInput < 1 | stockbankInput > 3)
{
if(!firstTry)
Console.WriteLine("Error: Please enter either 1, 2 or 3");
firstTry = false;
Int32.TryParse(Console.ReadLine(), out stockbankInput);
}
}
First of all, don't use goto statements. They are considered bad practice, and it's like a blinding red light when reading your question - that's all I can focus on.
As per your question, an int or Int32 cannot be null. So you can't compare it to null. Give it a default value, and then check that.
This is a scenario where you don't need to check for an error, but just need to validate input. Use TryParse, which will set your out parameter if the parse is successful, or else set it to 0.
Next, you want to loop until you are given good input. An if statement is executed once, a loop will guarantee that when you leave it, your input will be valid.
Lastly, the firstTry is just a nice way to let the user know, after their first try, that they screwed up.
if (((turn % 2) != 0) && (vsComputer))
{
int generateAI = generateRandomAI(AI);
switch (generateAI)
{
case 0:
computerMedio();
break;
case 1:
computerDifficile();
break;
}
}
I want my 0/1 value to be the same for all the game.
With the code I wrote, on every move it selects a different AI mode.
It shouldn't be that hard to achieve, but I can't find a solution.
Thanks everyone!
Your issue is:
int generateAI = generateRandomAI(AI);
You are generating a new random value on every invocation of this code block. If you wish to keep the same value for all execution, just generate the value once and keep it in scope.
Ok I solved in the following way
public Boolean difficult;
in the newGame() method I generate the number which lasts for all the game.
then,
switch (generateAI)
{
case 0:
difficult = false;
break;
case 1:
difficult = true;
break;
}
into the game:
if (((turn % 2) != 0) && (vsComputer))
{
if (difficult)
{
computerDifficile();
}
else
{
computerMedio();
}
}