Im trying to learn loops and they are on the verge of breaking me. This loop has either not worked at all or never ended which has lead to some serious stress. If anyone could help me out it would be great. The plan is for the loop to continue until someone writes yes/Yes/YES (any form of yes preferably) and then break and continue the next part of the code which is some readlines and write lines, haven't gotten that far because the loop hasn't let me yet. Very thankful for any input.
Console.WriteLine("Hello Inspector!");
Console.WriteLine("Are you ready to identify the suspects? Just write yes and we'll get started.");
string Start = Console.ReadLine();
Startup();
while (Start.Contains("")==false)
{
Console.WriteLine("Just type yes when you are ready.");
if (Start.Contains("Yes") == true)
Console.WriteLine("Let's start.");
break;
}
}
static void Startup()
{
string Start = Console.ReadLine();
if (Start.Contains("yes") == true)
{
Console.Clear();
Console.WriteLine("Here are the suspects:");
}
else
{
Console.WriteLine("Just type yes when you are ready.");
}
}
}
}
There are several issues with your code:
1) You only once read user input - as m.sh already noted, you need to put
Start = Console.ReadLine();
inside your while loop.
2) Your break you expect only to catch if your condition is met is outside the scope because you are missing enclosing { } like this:
if (Start.Contains("Yes") == true)
{
Console.WriteLine("Let's start.");
break;
}
3) Not directly a programming bug but widely frowned upon: explicitly comparing boolean. Simply use
if (Start.Contains("yes"))
instead of
if (Start.Contains("yes") == true)
4) Also already mentioned - use .ToLower() to allow any input casing
if (Start.ToLower().Contains("yes"))
will work for yes, YES, yEs, YeS, ...
Putting together the parts for a working loop
// many coding guidelines ask you to use string.Empty rather than "". [I.]
string Start = string.Empty;
while (!Start.ToLower().Contains("yes"))
{
Console.WriteLine("Just type yes when you are ready.");
Start = Console.ReadLine();
}
Console.WriteLine("Let's start.");
Note the negation ! for the while condition - this makes your loop run as long as the condition is not met instead of having to check inside your loop if you need to break out.
Another way to loop could be do { } while(); where your condition is checked at the end of the loop:
string Start = string.Empty;
do
{
Console.WriteLine("Just type yes when you are ready.");
Start = Console.ReadLine();
}
while (!Start.ToLower().Contains("yes"));
If you step through your code running in debugger, you will notice the different behavior and how do {} while() can be considered faster code than while() { }.
I. In C#, should I use string.Empty or String.Empty or “” to intitialize a string?
Related
I couldn't find the answer to this anywhere, so what I want to do is first, ask the user a question such as "do you want to see this cool movie?" and if the user replies yes then I show them the movie and if they reply no then I move on. If the user replied with something other than yes or no, such as "fadlfjlashfashdf", I wanted to be able to ask the user "Please answer yes or no." until they answer yes or no. The problem is, I couldn't find a way to do that without making the code very confusing and long. Here is something that I tried:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Do you want to see this cool move?");
string answer = Console.ReadLine();
while(answer.ToLower() != "yes" || answer.ToLower() != "no")
{
Console.WriteLine("Please type yes or no. ");
answer = Console.ReadLine();
}
if (answer.ToLower() == "yes")
{
Console.WriteLine("ok");
}
else if(answer.ToLower() == "no")
{
Console.WriteLine("sure");
}
}
}
You could use the TryXxx pattern made famous by calls like double.TryParse and Dictionar<TKey, TValue>.TryGetValue. First write a TryXxx method like:
static bool TryGetBooleanFromAnswerString(string answer, out bool isAffirmative)
{
if (answer.Equals("yes", StringComparison.OrdinalIgnoreCase))
{
isAffirmative = true;
return true;
}
if (answer.Equals("no", StringComparison.OrdinalIgnoreCase))
{
isAffirmative = false;
return true;
}
isAffirmative = false; //it's meaningless, but I need to set it to something
return false;
}
Notice that I used the string.Equals overload that takes a StringComparison parameter - it says that "Yes", "yes" and "YES" are all equivalent.
Then I can write code like:
bool answerAsBool;
do
{
Console.WriteLine(#"Wanna take a bath"); // I'm old enough to remember Pink Floyd
} while (!TryGetBooleanFromAnswerString(Console.ReadLine(), out answerAsBool));
Console.WriteLine($#"You answered with a {answerAsBool} answer");
Your approach here is perfectly reasonable. In fact, it's almost exactly how I'd do it.
In general, if there's some condition that needs to be handled in a special way (like invalid input), and it can happen multiples times in a row (the user continues to give invalid input), then you're gonna have to use a loop of some kind.
Now, with that said, there's a bug in your code. Can you find it?
I want to lock few lines of code based on a flag from App config. So based on that flag I run the application asynchronously or not. So i need to lock execution of few lines of code by checking the flag. So i need to write code repetitive. Below is the sample
if (flag) {
lock(dataLock){
//few lines of code
}
} else {
//repeat the above code gain here (few lines of code)
}
Is there any alternative way where I can save my repeated codes.
if (flag)
Monitor.Enter(dataLock);
// few lines of code
if (Monitor.IsEntered(dataLock))
Monitor.Exit(dataLock);
Use Monitor.Enter instead of Lock() {} ? Enter and exit with the if statement.
You could call a function with your outsourced code from within the lock and even from within the else statement. That would at least reduce your overhead and repetitive code.
if(flag==true){
lock(dataLock){
fewLines();
}
}else{
fewLines();
}
[...]
public void fewLines(){
// put your few lines here.
}
that would run the function from the locked context.
The Monitor.Enter aproach is best, but you could also do this:
Action fewLinesOfCode = () =>
{
//few lines of code
};
if (flag)
{
lock (dataLock)
{
fewLinesOfCode();
}
}
else
{
fewLinesOfCode();
}
Why when I type "gamble" the first time, only the if statement works? It's no use typing anything else, it still adds 10 woods. And why when I type anything else the first time, just the else statement works? It is no use typing "gamble, will continue saying " Write '' gamble '' to hit the tree. " PS: The variable its int = woods; and string gamble;
Console.WriteLine("You have {0} woods", woods);
Console.WriteLine("Write ''gamble'' to hit the tree");
gamble = Console.ReadLine();
bool loop = true;
while (loop)
{
if (gamble.Contains("gamble"))
{
woods = woods + 10;
Console.Clear();
}
else
{
Console.WriteLine("Write ''gamble'' to hit the tree");
}
Console.WriteLine("You have {0} woods", woods);
Console.ReadLine();
}
gamble = Console.ReadLine();
You only set gamble in the beginning. In the loop it is never changed. So it keeps using the first value over and over again.
Add gamble = to the last line of the loop.
If I understand what your are describing, you forgot to read into gamble again
Console.WriteLine("You have {0} woods", woods);
gamble = Console.ReadLine();
}
At the end of the while loop, you are doing Console.ReadLine() but not storing it. You need gamble = Console.ReadLine() to store the scanned string in the "gamble" variable.
Because loop is always true. You should change it to false after if and else statements...
The reason it's adding 10 wood regardless if there is something else than "gamble" in the console line, is because you're writing "gamble" in the returning message.
else {Console.WriteLine("Write ''gamble'' to hit the tree");} is the problem here.
You can fix it by either not writing the word "gamble" inside the returning message, or find a clever way to not have it run in a while(true) loop.
You can, for example, use the main method to have it run the function you're going to run just once.
Something like this.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
// Set a `wood` variable for the class.
protected int wood { get; set; }
static void Main(string[] args)
{
Program program = new Program(); // Making use of non-static methods.
program.Handler();
}
public void Handler()
{
Console.WriteLine("Write \"gamble\" to hit the tree.");
string message = Console.ReadLine();
if (message == "gamble")
{
addWood(); // Call the non-static method.
}
}
public bool addWood()
{
this.wood = this.wood + 10;
Console.WriteLine("You now have {0} wood!", this.wood);
Handler(); // Call the Handler() method again for infinite loop.
return true;
}
}
}
WARNING: The program will exit if there is something else than "gamble" written.
Today I came across a problem: I was trying to check the errors of a software in order to provide the right behavior of the program when it incurs in the error.
I had to check if a user already exists in the database.
The problem is that the back-end doesn't provide an errorId so I have to check the errors by the text.
Errors are displayed as this:
The user Name already Exists!
The Switch statement is this:
switch (error.text)
{
case "User Test already exists":
Console.WriteLine("The user already Exists"); //this is a test behaviour.
break;
default:
Console.WriteLine("I couldn't behave in any way :<");
}
As you can imagine the names are all different (it's a unique field in the DB), so the word "Test" in the case statement should be the name of the user.
Can I dynamically change the string?
Seems like a Regex would do the trick. I've built this Regex based off the pattern:
The user Name already Exists!
where Name can be any value. The Regex is:
(the user .* already exists)
To use it you'll do something like this:
Regex.IsMatch(error.text, "(the user .* already exists)", RegexOptions.IgnoreCase)
Which would return a true or false based on the match. Now, this can't be done in the switch, but you could just run the value through a number of Regexes to determine which it matched. One thing you might consider is an extension method. Consider this one:
public static class RegexExtensions
{
private static readonly Regex UserNameAlreadyExists = new Regex("(the user .* already exists)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static bool IsUserNameAlreadyExists(this string inputValue)
{
return UserNameAlreadyExists.IsMatch(inputValue);
}
}
The usage for this would be really nice:
if (error.text.IsUserNameAlreadyExists())
{
// do something
}
The extension method is a really nice way of working through it. It's fully encapsulated and would keep the usage really clean. Furthermore, it's easier to define the Regex in one place and thus set it to Compiled making it faster.
Preferably change the back-end or have it changed (it definitely should return some sort of error code instead of an already localized message obviously meant to be shown to the user - that's clearly a front-end task).
To answer the question, no; consider using something like this instead (original phrasing, be aware that these string comparisons are case sensitive):
if(error.text.StartsWith("User ") && error.text.EndsWith(" already Exists"))
{
Console.WriteLine("The user already Exists"); //this is a test behaviour.
}
else
{
Console.WriteLine("I couldn't behave in any way :<");
}
I suppose this would be a fairly simple solution:
class Program
{
int errorIndex = 5; //Based on error expected text. Can add more criteria here.
private static bool testResponse = false;
static void Main(string[] args)
{
string text = "The user already exists";
getErrorMessage(text);
}
private static void getErrorMessage(string message)
{
var user = message.Substring(4, 4);
var exists = message.Substring(17, 6);
if (user == "user" && exists == "exists")
//Write the error message.
Console.WriteLine(message.ToString());
var errorMessage = message;
if (errorMessage != null)
{
testResponse = true;
}
Console.ReadLine();
}
}
This is if you know the exact location of the length and index of certain words of the error message. You could use that information to further narrow down the errors you expect. This is assuming that there is no errorId.
just to be sure: The back-end doesn't provide any errorID? If you use C# for the database connection (i.e. ADO.Net) you have possibilitys for efficent error handling.
Is it possible to just check if error.text is empty or not?
if(error.text=="")Console.WriteLine("The User already exists");
else Console.WriteLine("I couldn't behave in any way");
If you want to check if there are duplicates in the "user" column you could check the database directly via SQL.
so I'm writing a program that takes in a text line by line and then is supposed to output yes or no if the line contains the word problem.
The program is working, but I have to press enter twice to get the output.
The first enter I get, which is for the last line. And the second enter is so the while loop can break out.
Any suggestions to how I can improve this and not need the second enter?
using System;
using System.Collections.Generic;
namespace Tester
{
internal class Program
{
private static void Main(string[] args)
{
List<string> stringList = new List<string>();
string input = "";
while ((input = Console.ReadLine()) != string.Empty)
{
var s = input.ToLower();
stringList.Add(s.Contains("problem") ? "yes" : "no");
}
foreach (var str in stringList)
Console.WriteLine(str);
Console.ReadKey();
}
}
}
Well, for the last output you will type something. That's when (input = Console.ReadLine()) != string.Empty kicks in and the condition will pass.
The loop will come back to this line and block until you give it new input. Then supposedly you just type enter and in that case the loop will just exist. This is expected behaviour.
I'm not sure what upsets you about this. If you reaaaally wanted to get rid of the second enter, maybe you can put some token in your line (line /q or whatever) and whenever that is found in your line you know that you should break out of the loop.
Alternatively you can count how many inputs you get and make sure you get exactly 10 or 20 or whatever. When that number is reached, the loop will exit after the last input is processed.
Welcome to SO. :)
You can safely get rid of the last ReadKey. Given that you're creating a console application, you would normally rut it... in a console - as such, consoles don't close themselves after a program is done running. It's different if you run a console application in Windows OUTSIDE of a console - in this case, Windows will open a temporary console, run the app, and then close the console.
Also, if you're using Visual Studio, you can make VS wait for you by using the "start without debug" option (Ctrl+F5). VS will then add a "press enter to close" on it's own, at the end, to prevent the window from closing too fast, allowing you to check your outputs / exceptions.
One simple solution could be to output the "yes" or "no" values per line, rather than all at once at the end of your app.
Another way (which would require a bit more coding) would be to read individual keys, rather than lines - then you could react to the user pressing Esc, for example, rather than relying on an empty string ("extra" enter press).
You can use string.IsNullOrEmpty() and ToUpper() method is in general more accurate than ToLower(). So i'd probably refactor your code to something like :
private static void Main(string[] args)
{
List<string> stringList = new List<string>();
string input = "";
while(!string.IsNullOrEmpty(input = Console.ReadLine()))
stringList.Add(input.ToUpper().Contains("PROBLEM") ? "yes" : "no");
foreach (var str in stringList)
Console.WriteLine(str);
Console.ReadKey();
}
By the way welcome to SO... ;)
ok,do you mean this
static void Main(string[] args)
{
string input = "";
input = Console.ReadLine();
while (input != string.Empty)
{
if (input.Contains("problem"))
{
Console.WriteLine("yes");
}
else
{
Console.WriteLine("no");
}
input = Console.ReadLine();
}
Console.ReadKey();
}
maybe I know your intention.But if you want to break the while(input!=string.Empty) you must press an empty line.