I am making a music maker program in C# (visual studio).
Here is my code:
int accCount = 0;
enum accidental { flat, sharp, none }
accidental thisAcc = accidental.none;
if (keyComboBox.SelectedItem.ToString().Length < 8)
{
MessageBox.Show("Please select a key!");
}
else switch (keyComboBox.SelectedItem.ToString())
{
case "C major - A minor":
accCount = 0; thisAcc = accidental.none;
break;
case "G major - E minor":
accCount = 1; thisAcc = accidental.sharp;
break;
...etc..
}
and so on...This all is included in postButton_click(postButton_Click(object sender, EventArgs e)
But when I click the button, an exception is shown (An unhandled exception of type 'System.StackOverflowException' occurred in Program.exe)And if I select "break", this line is selected:
object key(int count, accidental ac) (here is the cursor){
return key(0, accidental.none);
}
Does anyone know what is wrong?Sorry if this question is not specific enough, just tell me.
Well yes, look at this code (reformatted from your question for readability):
object key(int count, accidental ac)
{
return key(0, accidental.none);
}
That will just invoke the same method... which will invoke the same method... which will invoke the same method etc, until it runs out of stack space.
It's not clear what you intended to return from this method, but you need to stop recursing in this infinite way.
A stackoverflow means that you have some unbound recursion in your application. In English this essentially means you're calling a method again and again.
Can you see how you're calling your method key within itself?
Related
I'm modifying existing C# code in order to pilote a piston. Every 30ms, I have a direct feedback of the position of this piston, through an event. The value is stored in a global variable I use to get the current position of the piston.
What I'm trying to achieve: for a given distance input (A->C), I want the piston to travel at full speed for 95% of the distance (A->B), and then slower for the remaining 5% (B->C).
I have access to a command that defines the speed and the destination of the piston : pos(velocity, destination).
However, if I write that code:
pos(fullSpeed,B);
pos(reducedSpeed, C);
the piston directly goes from fullSpeed to reducedSpeed
I tried to use a while loop to compare the current position of the piston with the goal destination, however, upon entering the while loop, the variable storing the piston position does not update anymore.
However, I noticed that by throwing a MessageBox in between, the position value keeps on getting updated, and I can simply click "ok" to launch the second command.
pos(fullSpeed,B);
MessageBox.show("Wait");
pos(reducedSpeed, C);
I would like to know why the "while" loop stops the update of the position variable but the MessageBox does not. I mean, as long as I don't click the "ok" button, the box is here preventing me from doing anything, which for me ressembles a while loop behaviour. Is there another way for me to do this instead of the MessageBox ?
I have little to no knowledge when it comes to C# and no support. I have tried to look in the documentation, but I did not find an answer (I have probably missed it). Any lead is more than welcome.
EDIT: I have no documentation for that code, and it is barely commented. Here is what I gathered (really hope it helps):
To move the piston, taht function is called:
MyEdc.Move.Pos(control, speed, destination, ref MyTan);
control simply define what we pilote (a distance or a load, it is an enum), and I have no idea what MyTan does. Only thing I know is that the MyEdc.Move.Pos returns an error code.
If I look at the definition of "pos", I am redirected to class
public DoPEmove Move;
containing among other things:
public DoPE.ERR Pos(DoPE.CTRL MoveCtrl, double Speed, double Destination, ref short Tan);
DoPE.ERR is also an type enum. However, I cannot reach the definition of a function named "Pos". Coud it be within the .dll included ?
The following is the code that allows me to access the position of the piston (without the global variables):
private int OnData(ref DoPE.OnData Data, object Parameter)
{
if (Data.DoPError == DoPE.ERR.NOERROR)
{
DoPE.Data Sample = Data.Data;
Int32 Time = Environment.TickCount;
if ((Time - LastTime) >= 300 /*ms*/)
{
LastTime = Time;
string text;
text = String.Format("{0}", Sample.Time.ToString("0.000"));
guiTime.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S].ToString("0.000"));
guiPosition.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F].ToString("0.000"));
guiLoad.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E].ToString("0.000"));
guiExtension.Text = text;
}
}
return 0;
}
Which is called using
MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData);
I realise how little I know on how the soft operates, and how frustrating this is for you. If you think this is a lost cause, no problem, I'll try Timothy Jannace solution, and if it does not help me, I'll stick with the MessageBox solution. I just wanted to know why the MessageBox allowed me to sort of achieve my objectif, but the while loop did not, and how to use it in my advantage here.
I tried to use a while loop to compare the current position of the
piston with the goal destination, however, upon entering the while
loop, the variable storing the piston position does not update
anymore.
While you are in the while loop, your app can no longer receive and process the feedback event.
One possible solution would be to use async/await like this:
private const int fullSpeed = 1;
private const int reducedSpeed = 2;
private int currentPistonPositon = 0; // global var updated by event as you described
private async void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
pos(fullSpeed, B);
await Task.Run(() =>
{ // pick one below?
// assumes that "B" and "currentPistonPosition" can actually be EXACTLY the same value
while (currentPistonPositon != B)
{
System.Threading.Thread.Sleep(25);
}
// if this isn't the case, then perhaps when it reaches a certain threshold distance?
while (Math.Abs(currentPistonPositon - B) > 0.10)
{
System.Threading.Thread.Sleep(25);
}
});
pos(reducedSpeed, C);
}
Note the button1_Click method signature has been marked with async. The code will wait for the while loop inside the task to complete while still processing event messages because of the await. Only then will it move on to the second pos() call.
Thank you for your answer ! It works like a charm ! (good catch on the
EXACT value). I learnt a lot, and I am sure the async/await combo is
going to be very usefull in the future ! – MaximeS
If that worked well, then you might want to consider refactoring the code and making your own "goto position" method like this:
private void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
GotoPosition(fullSpeed, B);
GotoPosition(reducedSpeed, C);
}
private async void GotoPosition(int speed, int position)
{
pos(speed, position);
await Task.Run(() =>
{
while (Math.Abs(currentPistonPositon - position) > 0.10)
{
System.Threading.Thread.Sleep(25);
}
});
}
Readability would be greatly improved.
You could even get fancier and introduce a timeout concept into the while loop. Now your code could do something like below:
private void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
if (GotoPosition(fullSpeed, B, TimeSpan.FromMilliseconds(750)).Result)
{
if (GotoPosition(reducedSpeed, C, TimeSpan.FromMilliseconds(1500)).Result)
{
// ... we successfully went to B at fullSpeed, then to C at reducedSpeed ...
}
else
{
MessageBox.Show("Piston Timed Out");
}
}
else
{
MessageBox.Show("Piston Timed Out");
}
}
private async Task<bool> GotoPosition(int speed, int position, TimeSpan timeOut)
{
pos(speed, position); // call the async API
// wait for the position to be reached, or the timeout to occur
bool success = true; // assume we have succeeded until proven otherwise
DateTime dt = DateTime.Now.Add(timeOut); // set our timeout DateTime in the future
await Task.Run(() =>
{
System.Threading.Thread.Sleep(50); // give the piston a chance to update maybe once before checking?
while (Math.Abs(currentPistonPositon - position) > 0.10) // see if the piston has reached our target position
{
if (DateTime.Now > dt) // did we move past our timeout DateTime?
{
success = false;
break;
}
System.Threading.Thread.Sleep(25); // very small sleep to reduce CPU usage
}
});
return success;
}
If you're using events you are probably having concurrency issues. Especially with events being raised every 30ms!
A very simple way to handle concurrency is to use a lock object to prevent different threads from using contested resources simultaneously:
class MyEventHandler
{
private object _lockObject;
MyEventHandler()
{
_lockObject = new object();
}
public int MyContestedResource { get; }
public void HandleEvent( object sender, MyEvent event )
{
lock ( _lockObject )
{
// do stuff with event here
MyContestedResource++;
}
}
}
Keep in mind that is very simple and by no means perfect in every scenario. If you provide more information about how the events are raised and what you're doing with them people will be able to provide more help.
EDIT:
Using that signature you posted for the Pos method I was able to find documentation on the library you are using: https://www.academia.edu/24938060/Do_PE
The reason you only see the method signature when you goto definition is because the library has been compiled into a dll. Actually, it probably wouldn't be that useful to see the code anyway because it looks like the library is a C# wrapper around native (c or c++) code.
Anyways, I hope the documentation is helpful to you. If you look at page 20 there are some pointers on doing movement. This is going to be a challenge for a new programmer but you can do it. I would suggest you avoid using the event handler to drive your logic and instead stick with using the synchronous versions of commands. Using the synchronous commands your code should operate the same way it reads.
I believe what you'll want to do is add a call to:
Application.DoEvents();
This will allow your application to process posted messages (events), which will allow that global variable to be updated.
I just wanted to know why the MessageBox allowed me to sort of achieve my objectif, but the while loop did not, and how to use it in my advantage here.
The reason that works is because you're giving the WndProc a chance to process events which have been sent to the application. It's not an intended feature of that call to MessageBox.Show();, but it is a consequence. You can do the same thing with a call to Application.DoEvents(); without the interruption of the message box.
I don't want to use if statement. For simplicity and performance I want to use switch case and execute that method. I want all the keyboard Input to get detected. But is there any method that will pass any key press information?
My current approach is :
// Update is called once per frame
void Update()
{
char a = Input.GetKey();//anything like this method?
switch (a)
{
case 'a':
//print'';
break;
case 'b':
//print'';
break;
default:
break;
}
}
How can I achieve this any key press information detection without if statement?
Best Solution - Use Input.inputString
You may use Input.inputString to catch the keyboard input entered in the last frame as a string.
void Update()
{
//get the input
var input = Input.inputString;
//ignore null input to avoid unnecessary computation
if (!string.IsNullOrEmpty(input))
//logic related to the char pressed
Debug.Log("Pressed char: " + Input.inputString);
}
Other solutions
Foreach Loop
You might use foreach loop approach, like suggested in other answers, but it is not as performant. Foreach allocates more memory and must check against all possible keys (it is also less readable).
If you do not care about time and memory performance, then you might follow this answer. It uses the foreach loop approach and create a reusable interface.
OnGUI
Finally you could catch the Event.current (that is a unityGUI Event), like explained in this answer, but doing so you would rely on the OnGUI method. This method has the worst performance.
foreach(KeyCode kcode in Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKey(kcode))
Debug.Log("KeyCode down: " + kcode);
}
Also you can cache the value of Enum.GetValues(typeof(KeyCode)) for optimization.
I've not used Unity, however, I understand your problem and I am a C# developer.
From a quick search I have found someone on Unity forums with a similar problem to you. Here is the thread https://answers.unity.com/questions/1520939/check-if-there-is-a-keyboard-input.html.
if (Input.anyKeyDown)
{
Event e = Event.current;
if (e.isKey)
{
Debug.Log("Current Key is : " + e.keyCode.ToString());
}
}
The code above (from the Unity forum link) enables you to detect input.anyKeyDown (keyboard and mouse). Then you can filter the mouse detections by checking if the input was only a keyboard input with e.isKey
Here is documentation for KeyCode. This also includes all the properties available to it (many keyboard related properties that you can potential check against).
For example (not tested):
Event e = Event.current;
if (e.isKey)
{
Debug.Log("Current Key is : " + e.keyCode.ToString());
if(e.keyCode == KeyCode.A) //checks if the keycode returned equals the 'A' key
{
// Do something
}
}
EDIT: As mentioned by the other answer you can try Input.inputString. According to the documentation insputString contains "Only ASCII characters". So for example you could do something like this in the 'Update' method if letters was what you were only looking to check.
void Update()
{
//get the input
var input = Input.inputString;
//ignore null input to avoid unnecessary computation
if (!string.IsNullOrEmpty(input))
{
switch(input)
{
case 'a': break;
case 'b': break;
}
}
}
Hope this can help.
I am very new to the programming world and recently dove into c#. I don't want to waste your time so I'll get right to it. I wanted to create a program just to test my knowledge, and thought I could attempt to execute specific blocks of code based on which key on the keyboard is pressed by the user. I tried doing this by creating an event handler that contained if statements, but then realized I didn't know how to have the event handler active in the program.
For example, and as you can see in the below snippet, after the WriteLine in Line 5 lets say I wanted to raise the EventKeyPress event so that it waits for user input and reads the key they have pressed and reacts accordingly, how would I do that?
Again, I'm almost a complete beginner and have searched around for explanations about event handlers for hours and still can't wrap my head around what I am supposed to do or if I am even using the event handler correctly. Thanks in advance!
static void Main();
{
if (search == "Ball")
{
Console.WriteLine("Press enter to exit or backspace to return to the search bar")
// RIGHT HERE
}
else
{
Console.WriteLine("Sorry, I don't recognize {0}", search);
}
void EventKeyPress(object sender, KeyPressEventArgs e)
{
for (int i = 0; i < 1;)
{
if (e.KeyChar == (char)Keys.Enter)
{
// exit app
}
else if (e.KeyChar == (char)Keys.Back)
{
// go back to search
}
else
{
i = 0; // error
}
}
}
}
So, you're asking for something that involves Threading which is not a beginner thing to accomplish at all. The best way to do this for a beginner is to ask for a prompt, then accept as an input. For example.
Console.WriteLine("Hello, what's your name?");
string nameStr = Console.ReadLine();
Console.WriteLine($"Hello, {nameStr}");
You can then use your variable and apply it to an if/while or whatever kind of conditional.
if (nameStr == "Matt"){
//Do This Code.
}
Once you have that code, add a sequential method that will ask the user to return to the main menu or whatever you want it to do.
Main.ReturnMenu(); //Or whatever you want to use.
I'm still new to programming, so every time i get an exeption i try to rewrite code so it is avoided. However in this case i see no way to work around it.
I have a textbox that the user uses to imput commands. The string(imput) is then split after the first space.
private void tbxMainImput_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
string commandText = tbxMainImput.Text.ToLower();
string[] commandTextSplitted = commandText.Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
ReadTextboxImput(commandTextSplitted);
tbxMainImput.ResetText();
e.SuppressKeyPress = true; //anoying beep is removed :)
}
}
this splitted string commandTextSplitted is being used by a switch method ReadTextboxImput
private void ReadTextboxImput(string[] imput)
{
switch (imput[0])
{
//some other cases
case "attack":
StartCombat(imput[1]); //trows exeption if user only types one word
break;
}
}
If a player only types one word, imput[1] doesnt exist, and an IndexOutOfRange exeption is cast(as it should). However the exeption seems unavoidable. the player can type one word and press enter...
so ive been trying some things to check for the exeption and then break out of the code but it doesnt seem to work. The msdn website isn't realy beginners friendly, and all i found on stackoverflow where people asking to find where the error came from. witch i know.
So far i have tried:
case "attack":
if (imput[1] == null) //(imput[1] == system.IndexOutOfRange) doesnt make sense but i had to try
{
rtbOutput.AppendText("Yes yes. Attack nothing...");
break;
}
StartCombat(imput[1]);
break;
i made a method that uses try and catch that worked, but it just detected the error, i wasn't able to do anything with it(like return a false, or something)
Any help would be apreciated.
you can check if the array length is > 1 instead of imput[1] == null
if (imput.Length > 1){
rtbOutput.AppendText("Yes yes. Attack nothing...");
break;
}
StartCombat(imput[1]);
break;
I have this problem with my code I cant fix.
I am making a quiz, but the questions mustnt be asked twice so I avoided that, but when it runs out of options it crashes.
It also could be the code after it, but I dont think so.
Here is my code:
private static void chooseQuestion()
{
Random randomQuestion = new Random();
int returnValue = randomQuestion.Next(1, 3);
switch (returnValue)
{
case 1:
if (randomValues.questionOneChosen != 1)
{
questionOne();
}else
{
chooseQuestion();
}
break;
case 2:
if (randomValues.questionTwoChosen != 1)
{
questionTwo();
}else
{
chooseQuestion();
}
break;
}
endQuiz();
}
and here is what is after it:
private static void endQuiz()
{
Console.Clear();
Console.WriteLine();
text.centered("You completed the QUIZ, well done!");
Console.WriteLine();
text.centeredWrite("Press ENTER to go back to the menu");
string input = Console.ReadLine();
if (input == "")
{
Menu.main();
}else
{
endQuiz();
}
}
If you need more code to help me, please ask me.
Thanks in advance!
A StackOverflowException on StackOverflow, nice!
Does Menu.main() end up calling chooseQuestion()?
When a function is called, a new entry on the stack is created with room for all its local variables and the address to return to when it returns. If a function calls itself (recursive call), its original stack entry is not released. If it calls itself often enough without returning, the stack will eventually run out of room and give this exception. Having said that, the stack is pretty big - I think the default is 1 megabyte - so you need a lot of recursive calls to exhaust it.
You've got two functions which both call themselves. If they do it often enough, you'll run out of stack space. Just replace the recursive calls with a loop. Here's your first function:
private static void chooseQuestion()
{
bool endQuizChosen = false;
while ( !endQuizChosen ) {
Random randomQuestion = new Random();
int returnValue = randomQuestion.Next(1, 3);
// ... the rest of the function ...
endQuizChosen = endQuiz();
}
}
and edit endQuiz() to not call itself but to return true if user wants to stop and false if they want to go on.
Your chooseQuestion method's logic goes like this:
a. Pick a 1 or 2, randomly
b. If 1, and I haven't asked question 1 yet, show question 1
c. if 1, and I have asked question 1, go to step (a)
d. repeat (b) and (c) for question 2
...
once you answer both questions, you try to get another random question. But there are no more questions available. So you recurse into an exception.
Don't write your logic this way. Use a collection, and remove the item from the collection after you've shown that question. When the collection is empty, you're done.
This is never getting set.
randomValues.questionOneChosen
So whatever it is, if it's not 1 or 2 the method is going to call itself again. It's probably always zero.