[C#] Hjälp mig förstå varför den här koden fungerar

Permalänk
Medlem

[C#] Hjälp mig förstå varför den här koden fungerar

Nedan visas en enkel meny som styrs med piltangenterna.

string[] menuOptions = new string[] { "One", "Two" }; int menuSelect = 0; while (true) { Console.Clear(); Console.CursorVisible = false; for (int i = 0; i < menuOptions.Length; i++) { Console.WriteLine((menuOptions[i] + (i == menuSelect ? "<--" : "")); } var keyPressed = Console.ReadKey(); if (keyPressed.Key == ConsoleKey.DownArrow && menuSelect != menuOptions.Length - 1) { menuSelect++; } else if (keyPressed.Key == ConsoleKey.UpArrow && menuSelect >= 1) { menuSelect--; }

Jag läser den så här:

När menuSelect och menuOptions båda har samma värde visas "<--" på den valda raden.

När programmet startas visas därmed en pil på första alternativet i menyn.

När DownArrow trycks in ökar både menuSelect och menuOptions med 1, varav en pil visas på alternativ två i menyn.

Om UpArrow nu trycks in går menuSelect tillbaka från 1 till 0, medan menuOptions ökar från 1 till 2 då det är den tredje iterationen av for-loopen.

Frågan: Hur kan programmet då se till att menuSelect och menuOptions har samma värde igen efter de två första iterationerna när det inte finns något som synkar menuSelect och menuOptions med varandra? menuOptions ökar ju blint för varje iteration, oavsett vilken tangent som trycks in.

Permalänk
Hedersmedlem

Hmm, jag vet inte exakt hur du menar men någonstans är det fel. menuOptions är en konstant array och ändras inte på något sätt någonstans i koden. Det enda som ändras är värdet på "menuSelect", som kan vara antingen 0 eller 1 här.

menuOptions är bara en array med två textsträngar, som den skriver ut (inuti for-loopen). Därtill kollar den värdet på menuSelect för att se om den skriver ut den just nu aktiva raden, och skriver isåfall ut en pil för att visa detta för användaren.

När man trycker nedåt så ökar värdet på menuSelect, om den inte redan är högsta tillåtna värde (menuOptions.Length - 1, dvs om du har 2 olika val så är 1 det högsta tillåtna -- eftersom arrayer indexeras från 0, så de tillåtna värdena är 0 och 1).

Jag vet inte om detta hjälper, återkom om inte.

Edit:
Koden är i princip likvärdig med att göra såhär istället för for-loopen:

if (menuSelect == 0) Console.WriteLine("One"); else if (menuSelect == 1) Console.WriteLine("Two");

... plus att den skriver ut pilen.

Visa signatur

Asus ROG STRIX B550-F / Ryzen 5800X3D / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + NAS / Corsair RM650x V3 / Acer XB271HU (1440p165) / LG C1 55"
NAS: 6700K/16GB/Debian+ZFS | Backup (offsite): 9600K/16GB/Debian+ZFS

Permalänk
Medlem
Skrivet av SweRobby:

Nedan visas en enkel meny som styrs med piltangenterna.

string[] menuOptions = new string[] { "One", "Two" }; int menuSelect = 0; while (true) { Console.Clear(); Console.CursorVisible = false; for (int i = 0; i < menuOptions.Length; i++) { Console.WriteLine((menuOptions[i] + (i == menuSelect ? "<--" : "")); } var keyPressed = Console.ReadKey(); if (keyPressed.Key == ConsoleKey.DownArrow && menuSelect != menuOptions.Length - 1) { menuSelect++; } else if (keyPressed.Key == ConsoleKey.UpArrow && menuSelect >= 1) { menuSelect--; }

Jag läser den så här:

När menuSelect och menuOptions båda har samma värde visas "<--" på den valda raden.

När programmet startas visas därmed en pil på första alternativet i menyn.

När DownArrow trycks in ökar både menuSelect och menuOptions med 1, varav en pil visas på alternativ två i menyn.

Om UpArrow nu trycks in går menuSelect tillbaka från 1 till 0, medan menuOptions ökar från 1 till 2 då det är den tredje iterationen av for-loopen.

Frågan: Hur kan programmet då se till att menuSelect och menuOptions har samma värde igen efter de två första iterationerna när det inte finns något som synkar menuSelect och menuOptions med varandra? menuOptions ökar ju blint för varje iteration, oavsett vilken tangent som trycks in.

Det verkar som om du missförstår "for" loopen och kanske "while" loopen.

Det som händer är att all kod i "while" körs om och om igen, och koden stannar inte någonstans för att vänta på en knapptryckning så som du kanske gjort med "Console.ReadLine()". Utan när vi kommer till if satsen så skippar koden helt enkelt förbi ifall upp eller ner inte tryckts ner sen senaste gången. Koden kommer till Console.ReadKey() stannar i väntan på att en tangent trycks ner.

for (int i = 0; i < menuOptions.Length; i++) { Console.WriteLine((menuOptions[i] + (i == menuSelect ? "<--" : "")); }

I for loopen så skapas "i" på nytt varje gång den körs, efter loopen så försvinner den då den går ur scope.

Det som händer är att konsolfönstret clearas, menyn skrivs ut, vänta på att användaren tryck upp eller ner(eller annan tangent), repetera. Och detta sker många gånger per sekund.

Edit: En array är immutable (oföränderlig), så menuOptions kommer innehålla två strängar tills du skapar en ny array och assignar den till menuOptions. Det du kan göra är att ändra strängarna i, med t.ex.:

menuOptions[0] = "foo"

Permalänk
Medlem

Nu gjorde jag ett dåligt exempel av en array. Tänk dig snarare att det är 5+ alternativ i denna.

Skrivet av Thomas:

Det enda som ändras är värdet på "menuSelect", som kan vara antingen 0 eller 1 här.

Men hur kan då menuSelect och menuOptions någonsin ha samma värde om den ena är dynamisk och den andra statisk?

Den här koden säger ju att menuSelect måste ha samma värde som i för att en rad ska få en pil:

(i == menuSelect ? "<--" : "")

Och eftersom menuSelect förändras för varje knapptryck så måste ju i:et som representerar menuOptions också förändras till samma värde för att någon rad ska få en pil över huvud taget.

Permalänk
Medlem
Skrivet av SweRobby:

Nu gjorde jag ett dåligt exempel av en array. Tänk dig snarare att det är 5+ alternativ i denna.

Men hur kan då menuSelect och menuOptions någonsin ha samma värde om den ena är dynamisk och den andra statisk?

Den här koden säger ju att menuSelect måste ha samma värde som i för att en rad ska få en pil:

(i == menuSelect ? "<--" : "")

Och eftersom menuSelect förändras för varje knapptryck så måste ju i:et som representerar menuOptions också förändras till samma värde för att någon rad ska få en pil över huvud taget.

Variabeln ’i’ räknar från 0 till antalet saker i ’meuOptions’ listan, varje gång for-loopen körs

I kommer då matcha med ett av värdena (precis den som är selected) och rita ut markören där.

Om en knapp just nu blivit nedtryckt så ändrat den vilken som är selected (if-satsen), den koden gör ingenting annars. Denna kod kan med lätthet skapa problem eftersom den som är selected lätt kan hamna långt utanför listan

Man kan till exempel göra
menuSelected = menuSelected % menuOptions.length
Efter if-satsen för att få den att börja om från toppen när man fortsätter nedåt eller tvärt om uppåt om man dar bort de extra jämförelserna på if-satserna

Sedan kör koden allt detta varje gång varv efter varv (while-loopen)

Rättningar
Permalänk
Medlem
Skrivet av swesen:

Det som händer är att all kod i "while" körs om och om igen, och koden stannar inte någonstans för att vänta på en knapptryckning så som du kanske gjort med "Console.ReadLine()". Utan när vi kommer till if satsen så skippar koden helt enkelt förbi ifall upp eller ner inte tryckts ner sen senaste gången.

Nej. Från manualen:

The ReadKey method waits, that is, blocks on the thread issuing the ReadKey method, until a character or function key is pressed.

Skrivet av SweRobby:

Men hur kan då menuSelect och menuOptions någonsin ha samma värde om den ena är dynamisk och den andra statisk?

Den här koden säger ju att menuSelect måste ha samma värde som i för att en rad ska få en pil:

(i == menuSelect ? "<--" : "")

Och eftersom menuSelect förändras för varje knapptryck så måste ju i:et som representerar menuOptions också förändras till samma värde för att någon rad ska få en pil över huvud taget.

Ja. Variabeln i kommer i for-loopen att gå från 0 till menuOptions.Length - 1 och för ett och endast ett värde på i så kommer i vara lika med menuSelect.

Edit: Om man trycker nerpil när man står på sista raden kommer menuSelect inte ökas. Ingen av if-satserna körs i det läget. Samma sak om man står på första valet och trycker upp-pil.

Permalänk
Medlem
Skrivet av KAD:

Nej. Från manualen:

The ReadKey method waits, that is, blocks on the thread issuing the ReadKey method, until a character or function key is pressed.

Ja. Variabeln i kommer i for-loopen att gå från 0 till menuOptions.Length - 1 och för ett och endast ett värde på i så kommer i vara lika med menuSelect.

Edit: Om man trycker nerpil när man står på sista raden kommer menuSelect inte ökas. Ingen av if-satserna körs i det läget. Samma sak om man står på första valet och trycker upp-pil.

Japp, jag är blind (läste på mobilen) så jag missade sekundära satserna i if-satsen

Permalänk
Medlem
Skrivet av KAD:

Nej. Från manualen:

The ReadKey method waits, that is, blocks on the thread issuing the ReadKey method, until a character or function key is pressed.

Ja. Variabeln i kommer i for-loopen att gå från 0 till menuOptions.Length - 1 och för ett och endast ett värde på i så kommer i vara lika med menuSelect.

Edit: medbor har rätt ovan, i den inklistrade koden vi ser hanteras inte om menuSelect blir för stort. Det kan möjligen hanteras längre ner, i kod som inte kommit med.

Du har helt rätt. Tänkte på ett projekt ifrån skolan där vi inte blockerade exekveringen med Console.ReadKey(), men det för att inte blockera så bör man använda:

if (Console.KeyAvailable){ var keyPressed = Console.ReadKey(); if (keyPressed.Key == ConsoleKey.DownArrow && menuSelect != menuOptions.Length - 1) { menuSelect++; } else if (keyPressed.Key == ConsoleKey.UpArrow && menuSelect >= 1) { menuSelect--; } }

Så ska rätta mitt svar.