Introduktionsuppgift
Tärningen är kastad!
För att kuna följa denna guide måste du ha installerat Visual Studio Code, .NET Core SDK samt C#-tillägget för Visual Studio Code.
Du ska följa ”steg för steg”-instruktionen i denna introduktionsuppgift och skapa en konsolapplikation med C# och Visual Studio Code. Applikationen ska slumpa i det slutna intervallet mellan 100 till 1000 tärningskast och presentera en frekvenstabell över förekomsten av ettor, tvåor, osv.
Steg 1
Öppna ett nytt kommandofönster, gå till katalogen där du vill skapa katalogen för din applikation (i denna guide C:\PlayGround
) och kör följande kommando.
dotnet new console -o DieRollsFrequencyTable
Kommandot dotnet
skapar en ny (new
) applikation av typen console
. Parametern -o
skapar en ny katalog med namnet DieRollsFrequencyTable
där applikationens samtilga kataloger och filer återfinns.
Exempelutskrift
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on DieRollsFrequencyTable\DieRollsFrequencyTable.csproj...
Restoring packages for C:\Playground\DieRollsFrequencyTable\DieRollsFrequencyTable.csproj...
Generating MSBuild file C:\Playground\DieRollsFrequencyTable\obj\DieRollsFrequencyTable.csproj.nuget.g.props.
Generating MSBuild file C:\Playground\DieRollsFrequencyTable\obj\DieRollsFrequencyTable.csproj.nuget.g.targets.
Restore completed in 137,43 ms for C:\Playground\DieRollsFrequencyTable\DieRollsFrequencyTable.csproj.
Restore succeeded.
Steg 2
Byt till den nya katalogen DieRollsFrequencyTable
och starta Visual Studio Code genom att köra följande kommandon.
cd DieRollsFrequencyTable
code .
Steg 3
Visual Studio Code öppnar aktuell katalog som innehåller det nya konsolprojekt. Det nya projektet innehåller filerna DieRollsFrequencyTable.csproj
och Program.cs
, samt katalogerna bin
och obj
.
Filer är organiserade i projekt och ett projekt kan innehålla flera filer som tillsammans utgör applikationen.
Filen Program.cs
innehåller klassen Program
med metoden Main
och därmed är minimikraven uppfyllda för att du nu ska kunna köra applikationen.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Steg 4
För att köra applikationen välj menykommandot Debug > Start Without Debugging, eller tryck ner tangentbordkombinationen Ctrl + F5
. (Du kan även köra kommandot dotnet run
i ett kommandofönster.)
Applikationen körs och strängen "Hello World!"
skrivs ut.
Hello World!
Steg 5
Applikationen ska simulera tärningskast. Ett tal mellan 1 och 6 kan representera ett tärningskast. Resultatet av ett tärningskast måste sparas i en variabel vars värde sedan presenteras i konsolfönstret.
Deklarera en variabel, med namnet roll, av typen int och initiera den till värdet 4.
Presentera variabelns värde.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int roll = 4;
Console.WriteLine(roll);
}
}
}
Steg 6
Testa applikationen genom att välj menykommandot Debug > Start Without Debugging, eller tryck ner tangentbordkombinationen Ctrl + F5
. (Gjorda förändringar sparas automatiskt i samband med att ett projekt exekveras).
4
Steg 7
Resultatet av ett tärningskast slumpas inte på något vis av applikationen utan är vad som kallas ”hårkodat” till värdet 4. För att slumpa ett värde i det slutna intervallet mellan 1 och 6 måste ett objekt av typen Random
användas.
Skapa och initiera en referensvariabel, med namnet die (som betyder tärning på engelska), av typen
Random
att referera till ett objekt av typenRandom
.Istället för att tilldela variabeln
roll
värdet4
ska variabeln tilldelas värdet som metodenNext()
returnerar. I detta fall kommerdie.Next(1, 7)
att returnera ett värde av typenint
i det slutna intervallet mellan 1 och 6.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
Random die = new Random();
int roll = die.Next(1, 7);
Console.WriteLine(roll);
}
}
}
Steg 8
Kör applikationen flera gånger och konstatera att värden mellan 1 och 6 skrivs ut i konsolfönstren.
Steg 9
Applikationen kan nu simulera ett tärningskast. Kravet är att mellan 100 och 1000 tärningskast ska kunna simuleras. Hur ska applikationen göra för att simulera t.ex. 273 tärningskast?
Det kan enklast göras genom att låta en ”for”-sats omsluta satserna som slumpar och skriver ut. (Deklarationerna av variablerna die
och roll
placeras lämpligen innan "for"-satsen så att de bara skapas en gång istället för 273 gånger.)
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
Random die = new Random();
int roll;
for (int i = 0; i < 273; i++)
{
roll = die.Next(1, 7);
Console.WriteLine(roll);
}
}
}
}
Steg 10
Antalet tärningskast är nu ”hårdkodat” till 273. Användaren av applikationen ska kunna ange ett heltal i det slutna intervallet mellan 100 och 1000.
Deklarera variabeln
count
.Skriv ut ledtexten "Ange antalet tärningskast [100-1000]: ".
Läs in och tolka heltalet användaren matat in och lagra värdet i
count
.Ersätt
273
medcount
i ”for”-satsens villkorsuttryck.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
int roll;
Console.Write("Ange antal tärningskast [100-1000]: ");
count = int.Parse(Console.ReadLine());
for (int i = 0; i < count; i++)
{
roll = die.Next(1, 7);
Console.WriteLine(roll);
}
}
}
}
Steg 11
Hur många gånger har du hittills sparat de ändringar du gjort? Inte någon gång? Huuu! Hög tid att klicka på knappen Save All (Ctrl + Shift + S
) som sparar alla öppna filer som har osparade ändringar. Ta för vana att spara ofta, t.ex. efter varje sats. Ctrl + S
, som sparar filen som har fokus, ska sitta i ryggmärgen.
Steg 12
Efter att ett antal förändringar är gjorda är det lämpligt att testa applikationen igen.
För att kunna mata in data applikationen efterfrågar måste du se till att applikationen körs i ett konsolefönster (inte DEBUG CONSOLE i Visual Studio Code).
Öppna den integrerade terminalen, aktivera TERMINAL och kör
dotnet run
.
Du kan även konfigurera launch.json
i katalogen .vscode
att använda den integrerade terminalen istället för den interna konsolen.
Öppna
launch.json
och ersätt"console": "internalConsole"
med"console": "integratedTerminal"
. Kör applikationen som vanligt medCTRL + F5
och välj TERMINAL.
Ange antal tärningskast [100-1000]: 10
2
5
6
5
2
2
1
3
3
4
Steg 13
Applikationen kan nu simulera av användaren angivet antal tärningskast. Men hur ska en frekvenstabell över tärningskasten kunna skrivas ut? För att kunna skriva ut en frekvenstabell måste applikationen räkna antalet framslumpade ettor, tvåor, o.s.v. En träning har sex sidor varför en variabel med plats för sex värden behövs, en så kallad array.
Ersätt satsen som deklarerar variabeln
roll
med en sats som skapar referensvariabelnfrequencyTable
, av typenint[]
, och tilldela den en referens till en array med sex element.En array har 0-baserat index, d.v.s. det första elementet har index 0, varför tal i det slutna intervallet mellan 0 och 5 nu måste slumpas fram. Satsen som tilldelar roll ett slumpat värde ska ersättas med en sats där det framslumpade värdet leder till att motsvarande elements värde i arrayen ökas med 1.
En ”foreach”-sats är lämplig att använda för att skriva ut elementens värden i arrayen.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
int[] frequencyTable = new int[6];
Console.Write("Ange antal tärningskast [100-1000]: ");
count = int.Parse(Console.ReadLine());
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
foreach (int value in freqeuncyTable)
{
Console.WriteLine(value);
}
}
}
}
Steg 14
Kör applikationen och konstatera att något som kan vara en frekvenstabell skrivs ut i terminalfönstret.
Ange antal tärningskast [100-1000]: 873
135
157
140
155
150
136
Applikationen uppfyller nu de löst ställda kraven och en frekvenstabell över ett antal tärningskast skrivs ut. Men hur är det med kvalitéten på användargränssnittet?
Användaren uppmanas att mata in ett heltal i det slutna intervallet mellan 100 och 1000. Men vad händer om användaren matar in något som inte kan tolkas som ett heltal? Vad händer om användaren matar in t.ex. 13? Ska det gå?
Presentationen av frekvenstabellen har en del övrigt att önska. Vad betyder 146? Vad betyder 132?
Allvarligaste problemet är första punkten ovan eftersom om användaren matar in något som inte kan tolkas som ett heltal så kraschar applikationen.
Steg 15
Eventuella fel i samband med inläsning av antalet tärningskast som ska göras måste hanteras. Satsen som sköter inläsningen måste ersättas av ”do-while”-satsen, som får omsluta satsen som skriver ut ledtexten.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
int[] frequencyTable = new int[6];
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!int.TryParse(Console.ReadLine(), out count));
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
foreach (int value in frequencyTable)
{
Console.WriteLine(value);
}
}
}
}
Steg 16
Testa applikationen och konstatera att applikationen inte längre kraschar. Det går fortfarande att mata in ett tal som inte är i det slutna intervallet mellan 100 och 1000!
Ange antal tärningskast [100-1000]: tvåhundratre
Ange antal tärningskast [100-1000]: 1
0
0
1
0
0
0
Steg 17
I samband med inläsningen måste validering ske av värdet så att det ligger i det slutna intervallet mellan 100 och 1000.
Villkorsuttrycket i ”do-while”-satsen måste kompletteras så att det undersöker om det inlästa värdet är större eller lika med 100 och att det är mindre eller lika med 1000.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
int[] frequencyTable = new int[6];
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!(int.TryParse(Console.ReadLine(), out count) &&
count >= 100 &&
count <= 1000));
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
foreach (int value in frequencyTable)
{
Console.WriteLine(value);
}
}
}
}
Steg 18
Vid test av applikationen ska det nu vara omöjligt att mata in något annat än ett heltal i det slutna intervallet mellan 100 och 1000.
Ange antal tärningskast [100-1000]: 59
Ange antal tärningskast [100-1000]: 1548
Ange antal tärningskast [100-1000]: etthundraåttifem
Ange antal tärningskast [100-1000]: 742
113
142
108
136
117
126
Den första punkten under steg 14 är nu åtgärdad. Återstår att åtgärda den andra punkten, d.v.s. att låta applikationen presentera frekvenstabellen på ett något bättre sätt så att t.ex. tärningssidan framgår:
Ettor: 107
Tvåor: 114
…
Sexor: 113
Steg 19
Applikationen måste känna till vad tärningssidorna kallas. Lämpligt kan då vara att låta en array innehålla de strängar som beskriver tärningens sidor. Då frekvenstabellen sedan presenteras kan respektive sträng skrivas ut tillsammans med resultatet.
Lägg till en sats som skapar referensvariabeln
faces
, av typenstring[]
, och initiera den med en array innehållande de sex strängar som var och en beskriver tärningens sidor.Ersätt ”foreach”-satsen med en ”for”-sats som skriver ut varje tärningssida med tillhörande värde i frekvenstabellen.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
int[] frequencyTable = new int[6];
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!(int.TryParse(Console.ReadLine(), out count) &&
count >= 100 &&
count <= 1000));
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($"{faces[i]}: {frequencyTable[i]}");
}
}
}
}
Det är inte optimalt med en lösning som kräver synkronisering av två olika arrayer. En bättre lösning kan t.ex. vara att använda en associativ array (”dictionary”), men den konstruktionen får vänta till längre fram i kursen.
Steg 20
Kör applikationen på nytt. Nu ska det tydligare framgå hur många ettor, tvåor, o.s.v. som har slumpats fram. Lägg märke till att tabellen är något svår att läsa då en-, tio- och hundratal inte står under varandra.
Ange antal tärningskast [100-1000]: 742
Ettor: 121
Tvåor: 122
Treor: 114
Fyror: 140
Femmor: 128
Sexor: 117
Steg 21
Värden (strängar, heltal, etc.) som skrivs ut med hjälp av en formatspecificerare kan justeras på olika sätt. Den förändrade WrtiteLine
-satsen ser till att tärningssidan vänsterjusteras med en fältbredd på sex tecken, och resultatet högerjusteras med en fältbredd på fyra tecken.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
int[] frequencyTable = new int[6];
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!(int.TryParse(Console.ReadLine(), out count) &&
count >= 100 &&
count <= 1000));
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($"{faces[i],-6}: {frequencyTable[i],4}");
}
}
}
}
Steg 22
En körnng av applikationen visar att den nu beträffande användargränssnittet är acceptabel, men har kanske en hel del övrigt att önska, bl.a. saknas felmeddelande vid felaktig inmatning.
Ange antal tärningskast [100-1000]: sexhundra
Ange antal tärningskast [100-1000]: 1200
Ange antal tärningskast [100-1000]: 42
Ange antal tärningskast [100-1000]: 600
Ettor : 105
Tvåor : 87
Treor : 98
Fyror : 97
Femmor: 108
Sexor : 105
Applikationen uppfyller nu det löst ställda kraven bättre beträffande användargränssnittet. Men hur är det med kvalitéten beträffande koden?
All kod är skriven i en och samma metod. Ofta vinner även mindre applikationer på att omstruktureras i flera metoder. Det blir då lättar att förstå och därmed underhålla koden.
Avsaknaden av kommentarer är total vilket försvårar förståelse av koden.
Steg 23
Placera koden som har hand om inläsningen av antal tärningskast som ska simuleras i en separat metod med namnet ReadNumberOfRolls()
. Metoden anropas och värdet metoden returnerar lagras i den lokala variabeln count
.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
Random die = new Random();
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
int[] frequencyTable = new int[6];
count = ReadNumberOfRolls();
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($"{faces[i],-6}: {frequencyTable[i],4}");
}
}
private static int ReadNumberOfRolls()
{
int count;
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!(int.TryParse(Console.ReadLine(), out count) &&
count >= 100 &&
count <= 1000));
return count;
}
}
}
Steg 24
Även skapandet av frekvenstabellen och presentationen av den kan omstruktureras i två separata metoder.
Trots att koden fortfarande inte innehåller några kommentarer är den lättare att förstå då metodernas namn har valts med omsorg och beskriver väl vad respektive metod har till uppgift.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
class Program
{
static void Main(string[] args)
{
int count;
int[] frequencyTable;
count = ReadNumberOfRolls();
frequencyTable = CreateDieRollsFrequencyTable(count);
ViewFrequencyTable(frequencyTable);
}
private static int[] CreateDieRollsFrequencyTable(int count)
{
int[] frequencyTable = new int[6];
Random die = new Random();
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
return frequencyTable;
}
private static int ReadNumberOfRolls()
{
int count;
do
{
Console.Write("Ange antal tärningskast [100-1000]: ");
} while (!(int.TryParse(Console.ReadLine(), out count) &&
count >= 100 &&
count <= 1000));
return count;
}
private static void ViewFrequencyTable(int[] frequencyTable)
{
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($"{faces[i],-6}: {frequencyTable[i],4}");
}
}
}
}
Steg 25
Då användaren råkar mata in något som inte kan tolkas som ett heltal i det slutna intervallet mellan 100 och 1000 visas inget felmeddelande. Metoden ReadNumberOfRolls()
måste skrivas om så att så sker.
Program.cs - ReadNumberOfRolls
private static int ReadNumberOfRolls()
{
int count;
while (true)
{
try
{
Console.Write("Ange antal tärningskast [100-1000]: ");
count = int.Parse(Console.ReadLine());
if (!(count >= 100 && count <= 1000))
{
throw new ApplicationException();
}
return count;
}
catch (Exception)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\nFEL! Ange ett heltal mellan 100 och 1000.\n");
Console.ResetColor();
}
}
}
Steg 26
Kör applikationen och att mata in några felaktiga värden. Felmeddelande visas då användaren matar in något som inte kan tolkas som ett heltal i det slutna intervallet mellan 100 och 1000.
Ange antal tärningskast [100-1000]: tvåhundra
FEL! Ange ett heltal mellan 100 och 1000.
Ange antal tärningskast [100-1000]: 42
FEL! Ange ett heltal mellan 100 och 1000.
Ange antal tärningskast [100-1000]: 1300
FEL! Ange ett heltal mellan 100 och 1000.
Ange antal tärningskast [100-1000]: 240
Ettor : 49
Tvåor : 40
Treor : 31
Fyror : 36
Femmor: 41
Sexor : 43
Steg 27
Presentationen av frekvenstabellen behöver åtgärdas så den liknar en tabell.
Program.cs - ViewFrequencyTable
private static void ViewFrequencyTable(int[] frequencyTable)
{
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\n----------------");
Console.WriteLine(" Frekvenstabell ");
Console.WriteLine("----------------");
Console.ResetColor();
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($" {faces[i],-7} | {frequencyTable[i],4}");
Console.WriteLine("----------------");
}
}
Steg 28
Kör applikationen och verifiera att frekvenstabellen nu påminner mer om en tabell än tidigare.
Ange antal tärningskast [100-1000]: 600
----------------
Frekvenstabell
----------------
Ettor | 87
----------------
Tvåor | 94
----------------
Treor | 109
----------------
Fyror | 102
----------------
Femmor | 101
----------------
Sexor | 107
----------------
Steg 29
Kod ska alltid dokumenteras. Nedan visar hur källkoden blir enklare att förstår när den kompletterats med kommentarer, vilka självklart ska skrivas samtidigt som koden skrivs och inte efter att all kod skrivits klart.
Program.cs
using System;
namespace DieRollsFrequencyTable
{
/// <summary>
/// Applikationen simulerar tärningskast och presenterar utfallet i
/// form av en frekvenstabell.
/// </summary>
class Program
{
/// <summary>
/// Startpunkt för applikationen.
/// </summary>
/// <param name="args">Argument som kan skickas till applikationen (används inte).</param>
static void Main(string[] args)
{
// Deklarerar lokala variabler.
int count;
int[] frequencyTable;
// Läser in hur många tärningskast som ska simuleras då
// en frekvenstabell över tärningskast skapas och presenteras.
count = ReadNumberOfRolls();
frequencyTable = CreateDieRollsFrequencyTable(count);
ViewFrequencyTable(frequencyTable);
}
/// <summary>
/// Simulerar tärningskast, skapar och returnerar frekvenstabell över utfallet.
/// </summary>
/// <param name="count">Antal tärningskast att simulera.</param>
/// <returns>Array innehållande frekvenstabell över simulerade tärningskast.</returns>
private static int[] CreateDieRollsFrequencyTable(int count)
{
// Deklarerar lokala variabler.
int[] frequencyTable = new int[6];
Random die = new Random();
// Slumpar tärningskast och uppdaterar frekvenstabellen som därefter returneras.
for (int i = 0; i < count; i++)
{
frequencyTable[die.Next(6)]++;
}
return frequencyTable;
}
/// <summary>
/// Efterfrågar, läser in och returnerar antalet tärningskast som ska simuleras.
/// </summary>
/// <returns>Antal tärningskast.</returns>
private static int ReadNumberOfRolls()
{
// Deklarerar lokala variabler.
int count;
// Läser in och returnerar ett heltal mellan 100 och 1000.
while (true)
{
try
{
Console.Write("Ange antal tärningskast [100-1000]: ");
count = int.Parse(Console.ReadLine());
if (!(count >= 100 && count <= 1000))
{
throw new ApplicationException();
}
return count;
}
catch (Exception)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\nFEL! Ange ett heltal mellan 100 och 1000.\n");
Console.ResetColor();
}
}
}
/// <summary>
/// Presenterar en frekvenstabell över tärningskast.
/// </summary>
/// <param name="frequencyTable">Referens till frekvenstabell i form av en array
/// innehållade utfallet av tärningskast.</param>
private static void ViewFrequencyTable(int[] frequencyTable)
{
// Deklarerar lokal variabel.
string[] faces = { "Ettor", "Tvåor", "Treor", "Fyror", "Femmor", "Sexor" };
// Går igenom tärningssida för tärningssida och skriver ut tärningssidans
// namn samt antalet gånger sidan "kommit upp" vid ett tärningskast.
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\n----------------");
Console.WriteLine(" Frekvenstabell ");
Console.WriteLine("----------------");
Console.ResetColor();
for (int i = 0; i < faces.Length; i++)
{
Console.WriteLine($" {faces[i],-7} | {frequencyTable[i],4}");
Console.WriteLine("----------------");
}
}
}
}
Last updated