Gå till innehållet

Kapitel 7 - Metoder

Introduktion till kapitlet

När vi har gjort de större programmen som finns i projektuppgifterna i boken har du kanske märkt att det börjar bli svårt att hålla reda på all kod i programmmen. Programmen har helt enkelt blivit så långa att det blir väldigt mycket kod att hålla i huvudet på en gång och det blir svårt att snabbt se vad ett program ska göra. I detta kapitel ska vi se hur man kan skapa struktur i koden genom att dela upp sina program i metoder där varje metod sköter en liten del av programmet. När man läser koden i ett program som är uppdelat i metoder så behöver man bara hålla en liten del av programmet i huvudet i taget och det blir lättare att förstå hur programmets olika delar hänger ihop.

Förutom att skapa en bra struktur i ett programs kod så hjälper metoder också till genom att minska upprepning av samma kod. Om man har upprepat samma kod på olika platser i sitt program kan man flytta denna kod till en metod och då behöver man bara skriva koden på en plats istället.

Du har redan använt metoder i dina program, ända sedan du gjorde Hello World. Varje gång du har skrivit ett uttryck som avslutas med parenteser, t.ex. Console.WriteLine(), Console.ReadLine() eller int.Parse() så har du använt en metod. I detta kapitel kommer du att lära dig mer om hur metoder fungerar och är uppbyggda.

Metoder eller funktioner?

I en del andra programmeringsspråk så finns det något som kallas för funktioner vilka är väldigt lika metoder. Man kan säga att metoder och funktioner egentligen är samma sak med skillnaden att om en funktion ligger i ett class-block så kallas funktionen för en metod. Eftersom all kod vi skriver i C# alltid ska ligga inuti ett class-block så kallas alla funktioner i C# för metoder.

Skapa och använda en egen metod

Vi ska i vårt första exempel skapa en egen metod som hjälper oss att minska upprepning av samma kod i vårt program. Utan metoder så ser programmet ut som följer.

// Skriv det första meddelandet vertikalt
string meddelande1 = "Hej! ";
for (int i = 0; i < meddelande1.Length; i++)
{
    Console.WriteLine(meddelande1[i]);
}

// Skriv det andra meddelandet vertikalt
string meddelande2 = "Hej igen! ";
for (int i = 0; i < meddelande1.Length; i++)
{
    Console.WriteLine(meddelande2[i]);
}

// Skriv det tredje meddelandet vertikalt
string meddelande3 = "Hej då! ";
for (int i = 0; i < meddelande1.Length; i++)
{
    Console.WriteLine(meddelande3[i]);
}

Console.ReadKey();

Detta program skriver ut tre meddelanden vertikalt med hjälp av var sin for-loop på det sätt som vi har lärt oss i kapitlet om loopar. Just i detta program så hade man kunnat skriva ut all text vertikalt genom först sätta ihop alla de tre meddelandena i en ny strängvariabel och sedan skrivit ut den. Detta är inte möjligt i ett annat program, det hade t.ex. kunnat vara så att vi behövde skriva annan kod och annan text mellan de olika vertikala meddelandena. Vi tar inte med annan kod här för att exempelprogrammet ska vara så kort som möjligt. Så som det är nu innehåller programmet nästan samma kod på tre ställen, det enda som skiljer varje loop åt är vilken meddelande-variabel som används. När man har kod som är upprepad på detta sätt så kan man få bort mycket av upprepningen med hjälp av att skapa en metod.

När man skriver en egen metod så skapar man den utanför Main-metoden som vi hittills har skrivit all vår kod inuti. Vi kommer skapa en metod som heter SkrivVertikalt som man får skicka ett meddelande till, detta meddelande kommer att skrivas ut vertikalt. Det spelar ingen roll i vilken ordning metoderna i ett program kommer men vanligtvis så brukar man låta programmets Main-metod ligga överst.

static void Main(string[] args)
{

    SkrivVertikalt("Hej! ");
    SkrivVertikalt("Hej igen! ");
    SkrivVertikalt("Hej då! ");

    Console.ReadKey();
}

static void SkrivVertikalt(string meddelande)
{
    for (int i = 0; i < meddelande.Length; i++)
    {
        Console.WriteLine(meddelande[i]);
    }
}

Vi börjar med att analysera kodraden som säger vad metoden heter. Denna rad är

static void SkrivVertikalt(string meddelande)

Ordet static är ett ord du alltid måste skriva med först av allt när du skapar en egen metod tills vidare, även programmets Main-metod har detta ord framför sig.

Nästa ord, void, anger att denna metod gör någonting utan att skicka tillbaka ett svar. Void är engelska för ”tomrum” och menar helt enkelt att metoden inte ger något svar till den som använder den, du kommer senare i detta kapitel se hur man skriver metoder som kan skicka tillbaka svar till den som användar metoden.

Det som följer är metodens namn som är SkrivVertikalt vilket ska beskriva vad metoden gör. Avslutningsvis så skriver man en parentes och anger inuti den vilka variabler som ska skickas med till metoden när man använder den. Denna variabel, strängen meddelande, kallas för metodens parameter.

Innehållet i själva metoden är samma sorts loop som i exemplet tidigare som inte hade en egen metod. Variabeln som skrivs ut vertikalt är strängen meddelande, alltså parametern i metoden SkrivVertikalt. Om du skulle ha en variabel som heter meddelande i programmets Main-metod så skulle detta inte vara samma variabel som parametern meddelande, de skulle vara olika variabler eftersom de finns i var sin metod.

För att använda metoden så skriver man metodens namn följt av en parentes som i detta fall innehåller det meddelande som ska skriva ut vertikalt. Att använda en metod kallas för att anropa metoden. När metoden anropas genom att skriva SkrivVertikalt("Hej! ") så kallas strängen "Hej! " för metodens argument. Argumentet kopieras till metodens parameter som heter meddelande och koden inuti metoden kommer att köras. När metoden har kört klar så går programmet tillbaka till det ställe i koden där metoden anropades och fortsätter att köra där. Detta innebär att nästa rad som körs är SkrivVertikalt("Hej igen! "). Metoden anropas då med ett nytt argument, denna gång är argumentet "Hej igen! ". Detta argument kopieras till metoderns parameter meddelande och därefter körs koden i metoden på nytt. Slutligen så anropas metoden med argumentet "Hej då! ". Parametern meddelande kommer att ha olika värden de tre gånger som metoden anropas och om vi hade skapat någon variabel inuti metoden SkrivVertikalt så hade det värdet inte funnits kvar nästa gång metoden körs.

Parametrar och argument

Det är vanligt att man blandar ihop argument och parametrar och det gör ofta inte så mycket när man diskuterar programmering med någon annan. Sammanfattningsvis så gäller:

  • Parametern är variabeln inuti metoden som man själv skrivit.
  • Argumentet är värdet som man använder när man anropar metoden.
  • När en metod anropas så kopieras argumentet till parametern.

Mer om hur denna kopiering går till kommer i nästa kapitel.

Ytterligare en fördel med att använda metoder, förutom att vi slipper upprepa samma kod flera gånger, är att programmet blir mycket mer lättläst. Koden i Main-metoden blir inte så lång och eftersom metodnamnet beskriver vad metoden gör så förstår man lätt och snabbt vad programmet kommer att göra genom att bara läsa innehållet i Main. Vill man veta mer i detalj hur programmet skriver ut text vertikalt så kan man studera det i metoden SkrivVertikalt.

Nedan följer ytterligare ett exempel som visar att man slipper kodupprepningar och att koden blir mer lättläst när man använder metoder. Detta program har en metod som ritar ut kvadratar där metodens parameter anger hur stor kvadraten ska vara. Till skillnad från det förra exemplet så är parametern ett tal.

static void Main(string[] args)
{

    RitaKvadrat(4);
    Console.WriteLine();
    RitaKvadrat(3);
    Console.WriteLine();
    RitaKvadrat(6);

    Console.ReadKey();
}

static void RitaKvadrat(int sidlängd)
{
    for (int i = 0; i < sidlängd; i++)
    {
        for (int j = 0; j < sidlängd; j++)
        {
            Console.Write("X");
        }
        Console.WriteLine();
    }
}

Koden i Main-metoden blir mycket mer lättläst än om vi inte hade använt en metod.

Det är möjligt att ha mer än 1 parameter i en metod. Om en metod har flera parametrar så separeras de med kommatecken, även argumenten separeras med kommatecken när man anropar metoden. Metoden i följande exempel skriver en sträng det antal gånger som man anger att den ska skrivas.

static void Main(string[] args)
{
    SkrivUpprepadText("X", 30);
    SkrivUpprepadText("Hej", 15);

    Console.ReadKey();
}

static void SkrivUpprepadText(string text, int antalUpprepningar)
{
    for (int i = 0; i < antalUpprepningar; i++)
    {
        Console.Write(text);
    }
    Console.WriteLine();
}

Metoden har två parametrar som separeras med ett kommatecken.

Det går också bra att anropa en egen metod som man har skrivit inuti en annan metod som man har skrivit. Vi kan göra om RitaKvadrat-metoden från det tidigare exemplet så att den använder sig av SkrivUpprepadText-metoden. Detta kan göra programmet ännu lättare att förstå, ofta så kan program som använder sig av nästlade loopar bli lättare att förstå om man delar upp de nästlade looparna i metoder.

static void Main(string[] args)
{

    RitaKvadrat(4);
    Console.WriteLine();
    RitaKvadrat(3);
    Console.WriteLine();
    RitaKvadrat(6);

    Console.ReadKey();
}

static void RitaKvadrat(int sidlängd)
{
    for (int i = 0; i < sidlängd; i++)
    {
        SkrivUpprepadText("X", sidlängd);
    }
}

static void SkrivUpprepadText(string text, int antalUpprepningar)
{
    for (int i = 0; i < antalUpprepningar; i++)
    {
        Console.Write(text);
    }
    Console.WriteLine();
}

Uppgift 7.1

Skapa ett program som innehåller metoden SkrivTalIKvadrat(int tal). Metoden ska beräkna och skriva ut vad parametern tal är i kvadrat, alltså upphöjt till två. Om metoden anropas genom att skriva SkrivTalIKvadrat(5) så ska programmet skriva ut Talet 5 i kvadrat är 25. Anropa din metod minst 3 gånger i ditt program.

Lösningsförslag 7.1

Uppgift 7.2

Skapa ett program som innehåller metoden SkrivBaklänges(string meddelande). Metoden ska skriva ut parametern meddelande baklänges. Anropa metoden minst tre gånger i ditt program.

Lösningsförslag 7.2

Uppgift 7.3

Skapa ett program som innehåller metoden SkrivTalIKvadratMellan(int nedreGräns, int övreGräns). Metoden ska beräkna och skriva ut vad parametern tal är i kvadrat, alltså upphöjt till två. Om metoden anropas genom att skriva SkrivTalIKvadratMellan(3, 5) så ska programmet skriva ut

Talet 3 i kvadrat är 9.
Talet 4 i kvadrat är 16.
Talet 5 i kvadrat är 25.

Du får gärna kopiera in din metod från uppgift 7.1 till detta program och anropa den från din nya metod.

Lösningstips 7.3

Om du kopierar in din gamla metod i detta program så kan du anropa den inuti en loop i metoden SkrivTalIKvadratMellan(int nedreGräns, int övreGräns).

Lösningsförslag 7.3

Uppgift 7.4

Skapa ett program som innehåller metoden RitaRätvinkligTriangel(int sidlängd). Metoden ska rita ut en rätvinklig triangel med den sidlängd som anges. En triangel ritad av denna metod skulle t.ex. kunna se ut så här:

*
**
***
****
*****

Anropa metoden minst två gånger i ditt program.

Lösningstips 7.4

Det kan vara lämpligt att använda nästlade loopar i denna uppgift. Det kan vara bra att låta den inre loopen använda sig av den yttre loopens räknarvariabel på något sätt.

Lösningsförslag 7.4

Metoder i menyprogram

Menyprogram är bland de längre program som du har gjort hittills och desto längre de är, desto svårare blir det att läsa koden i programmet. Med hjälp av metoder kan man dela upp sitt program i mindre bitar som sköter en sak var. I detta exempel så skapas ett menyprogram där tre av programmets delar har flyttats till egna metoder. Ingen av dessa metoder har någon parameter, de finns bara för att programmet ska bestå av flera mindre bitar istället för en stor bit. Koden i programmets Main-metod är nu mycket kortare och det går snabbt att få en överblick av vad programmet gör. Vill man veta t.ex. hur text skrivs ut vertikalt i programmet så går man bara till den metoden och läser det där.

static void Main(string[] args)
{
    Console.WriteLine("Detta menyprogram kan skriva text på olika sätt");
    string användarensVal = "";
    while (användarensVal != "3")
    {
        SkrivMeny();
        användarensVal = Console.ReadLine();
        Console.WriteLine();

        switch (användarensVal)
        {
            case "1":
                MenyvalSkrivVertikalt();
                break;
            case "2":
                MenyvalSkrivUpprepadText();
                break;
            case "3":
                // Gör ingenting, programmet stängs av
                break;
            default:
                Console.WriteLine("Du valde ett ogiltigt alternativ");
                break;
        }
        Console.WriteLine();
    }
}

static void SkrivMeny()
{
    Console.WriteLine("Välj ett alternativ");
    Console.WriteLine("1. Skriv vertikalt");
    Console.WriteLine("2. Skriv upprepad text");
    Console.WriteLine("3. Avsluta");
}

static void MenyvalSkrivVertikalt()
{
    Console.WriteLine("Vilken text vill du skriva vertikalt?");
    string meddelande = Console.ReadLine();
    for (int i = 0; i < meddelande.Length; i++)
    {
        Console.WriteLine(meddelande[i]);
    }
}

static void MenyvalSkrivUpprepadText()
{
    Console.WriteLine("Vilken text ska upprepas?");
    string text = Console.ReadLine();
    Console.WriteLine("Hur många gånger ska texten upprepas?");
    int antalUpprepningar = int.Parse(Console.ReadLine());

    for (int i = 0; i < antalUpprepningar; i++)
    {
        Console.Write(text);
    }
    Console.WriteLine();
}

Menyprogrammets alternativ, att skriva text vertikalt och att skriva upprepad text, är tagna från exemplen tidigare i kapitlet. I de tidigare exemplen så hade metoderna SkrivVertikalt och SkrivUpprepadText en eller flera parametrar som bland annat angav vilken text som skulle skrivas ut, här blir detta inläst med Console.ReadLine() inuti metoden istället. Det är egentligen bra att försöka skapa metoder som har parametrar som styr de olika variablerna som metoden använder för att underlätta återanvändning av metoden. Dock brukar det passa bra att läsa in variablerna inuti metoderna istället när man skriver menyprogram om det inte är så att du vill återanvända en metod på flera ställen i ditt menyprogram.

Från och med nu bör du alltid dela upp dina program, såväl menyprogram som andra program, i minder metoder så att de blir lättare att läsa och förstå.

Uppgift 7.5

Skapa ett menyprogram med följande alternativ. Programmet ska vara uppdelat i metoder.

1. Addera två tal
2. Subtrahera två tal
3. Multiplicera två tal
4. Avsluta programmet
Lösningstips 7.5

Låt de metoder som tar hand om ett menyalternativ heta Menyval till att börja med, t.ex. MenyvalAddera och MenyvalSubtrahera.

Lösningsförslag 7.5

Skicka tillbaka svar med return

Det är möjligt att låta en metod skicka tillbaka ett svar till den plats där metoden anropades. I kod så används ordet return för att markera att en metod skickar tillbaka ett svar och därför passar det på svenska bra att säga att en metod kan returnera ett svar.

Metoderna vi hittills har skrivit har varit void-metoder, vi har alltid skrivit void direkt efter static när vi har deklarerat dem. En void-metod returnerar inget svar när metoden anropas. Om man vill att en metod ska skicka returnera ett svar så byter man ut void mot den variabeltyp som metoden ska returnera, t.ex. int. I exemplet nedan så skapas en metod som heter Addera som adderar de två tal som skickas som argument när metoden anropas, därefter returneras summan.

När metoden anropas med Addera(3, 5) i exemplet så får det samma effekt som om man direkt hade skrivit 3 + 5 direkt i koden. Eftersom man kan addera tal direkt med plustecknet så skulle man aldrig skriva denna metod i ett riktigt program, vi kollar på den här för att ha ett enkelt exempel att jobba med när vi lär oss hur en metod returnerar ett svar.

static void Main(string[] args)
{

    int summa = Addera(3, 5);
    Console.WriteLine(summa);

    Console.ReadKey();

}

static int Addera(int tal1, int tal2)
{
    return tal1 + tal2;
}

Eftersom metoddeklarationen ser ur så här

static int Addera(int tal1, int tal2)

så måste metoden returnera en int med hjälp av ordet return. Innan du har lagt till raden med return så kommer Visual Studio markera att din metod inte är korrekt skriven eftersom den inte returnerar en int, men det försvinner så fort du lägger till denna rad.

När metoden anropas med Addera(3, 5) så kommer argumentet 3 att kopieras till parametern tal1, argumentet 5 kommer att kopieras till parametern tal2. Metoden Addera har bara en rad som anger vad den ska returnera till den plats som anropar metoden. Denna rad säger att metoderna returnerar svaret av tal1 + tal2 till den som anropade metoden. Detta innebär att på raden

int summa = Addera(3, 5);

så kommer Addera(3, 5) att ersättas av talet 8 när metoden har anropats.

Varningar i Visual Studio

Så fort som du deklarerat en metod som inte har returntypen void så kommer en varning visas i Visual Studio. Denna varning säger att din metod inte returnerar något värde av den variabeltyp som den ska returnera. Det kan kännas irriterande att få denna varning men den är bra eftersom du då blir påminnd att returnera ett korrekt värde.

Vi ska kolla på ett till exempel på en metod som returnerar ett svar när den anropas. Denna metods deklaration börjar med static string vilket innebär att den alltid ska returnera en string när den anropas. Metoden returner en den sträng som skickas som argument till metoden när den anropas fast omvänd, t.ex. så blir ”Hej!” omvänt till ”!jeH”.

static void Main(string[] args)
{
    string omvändHälsning = VändSträng("Hejsan!");
    Console.WriteLine(omvändHälsning);
    Console.WriteLine(VändSträng("Hej igen!"));
    Console.WriteLine(VändSträng(omvändHälsning));

    Console.ReadKey();
}

static string VändSträng(string text)
{
    string omvändText = "";

    for (int i = text.Length - 1; i >= 0; i--)
    {
        omvändText += text[i];
    }

    return omvändText;
}

Metoden vänder på strängen som den tar emot genom att skapa en ny sträng där den omvända strängen ska sparas, därefter så går en loop igenom parametern text baklänges och lägger till ett tecken i taget till variabeln omvändText. I programmets Main-metod så anropas metoden tre gånger. Den första gången sparas den returnerade strängen i en variabel som sedan skrivs ut. Den andra och tredje gången skrivs den returnerade strängen ut direkt. Vid det tredje anropet av VändSträng så är argumentet en sträng som redan är omvänd, därför vänds den tillbaka till ”Hejsan!”.

Uppgift 7.6

Skapa ett program som innehåller metoderna

static double Multiplicera(double tal1, double tal2)

och

static double Dividera(double tal1, double tal2).

Metoderna ska multiplicera/dividera sina parametrar med varandra och returnera svaret. Anropa varje metod minst en gång och skriv ut den returnerade variabeln.

Lösningsförslag 7.6

Uppgift 7.7

Skapa ett program som innehåller metoden

static string Längst(string text1, string text2)

Metoden ska returnera den sträng av de båda parameterna som är längst. Som exempel så ska anropet Längst("Hej", "Hejsan") returnera "Hejsan". Om båda argumenten är lika långa så ska metoden returnera det första av dem.

Lösningsförslag 7.7

Uppgift 7.8

Skapa ett program som innehåller metoden

static int AntalISträng(char tecken, string text)

Metoden ska returnera antalet förekomster av tecknet tecken i strängen text. Som exempel så ska anropet AntalISträng('m', "Välkommen") returnera talet 2. Anropa metoden minst 2 gånger i ditt program.

Lösningstips 7.8

Använd en loop inuti metoden och gå igenom strängen tecken för tecken.

Lösningsförslag 7.8

Ett bättre sätt för konvertering av text till tal

Nu när du har lärt dig om loopar och metoder så kan vi kolla på ett bättre sätt att konvertera text till tal än int.Parse och double.Parse som vi har använt hittills. Ett problem med båda Parse-metoderna är att programmet kraschar om man försöker konvertera text som innehåller tecken som inte är siffror. Detta problem kommer vi att slippa när vi använder metoden TryParse istället. Dessutom så kan man veta om konverteringen lyckades eller inte och låta användaren fortsätta skriva in rader till hen har lyckats skriva in ett giltigt tal.

Vi kommer att börja med att göra ett enkelt program med TryParse som vi sedan gör om till bättre och bättre versioner. När du använder TryParse i fortsättningen så bör du göra det på det sätt som visas i det sista exemplet, men vi tar några enklare exempel först för att förstå hur TryParse fungerar.

TryParse skrivs lite annorlunda än Parse, TryParse returnerar nämligen inte svaret av konverteringen. Istället så får man skapa en variabel som ska innehålla det konverterade talet och skicka med den som ett argument tillsammans med ordet out när man anropar metoden. Ordet out gör så att själva variabeln skickas med till metoden istället för en kopia av variabeln, vilket är det som händer om man inte använder out. Vi kommer inte att stöta på out i några andra situationer i denna bok än när vi använder TryParse.

Console.WriteLine("Skriv in ett heltal");
int heltal;
int.TryParse(Console.ReadLine(), out heltal);

Console.WriteLine($"Du skrev in talet {heltal}");

Console.ReadKey();

Kör programmet några gånger och testa att dels skriva in tal och dels skriva in text som inte är tal. Om du skriver in någonting som inte är ett tal så kommer heltal att få värdet 0, men programmet kraschar inte.

Nyckelordet out

Det går att skriva egna metoder som har out-parametrar och med hjälp av detta låta en metod returnera mer än 1 värde. Detta är dock inte det sätt som man brukar föredra för att returnera mer än 1 värde från en metod, det vanligaste är att man skapar egna typer av class-variabler men detta ligger tyvärr utanför kursen Programmering 1.

Anledningen till att TryParse inte returnerar det konverterade heltalet är att den istället returnerar ett booleskt värde som anger om konvertering lyckades eller ej. Ett booleskt värde har ju antingen värdet true eller false och vi kan utnyttja en if-sats för att undersöka om konverteringen lyckades eller inte. I nästa version av exemplet skrivs bara talet ut ifall användaren skrev in ett giltigt tal eftersom koden i if-satsen bara körs om TryParse returnerar värdet true.

Console.WriteLine("Skriv in ett heltal");
int heltal;
if (int.TryParse(Console.ReadLine(), out heltal))
{
    Console.WriteLine($"Du skrev in talet {heltal}");
}
else
{
    Console.WriteLine("Du skrev inte in ett heltal");
}           

Console.ReadKey();

Vårt program har blivit bättre än det var från början, men om vi räknar med att användaren har skrivit in ett giltigt heltal så kan vi få problem längre fram i koden eftersom heltal får värdet 0 om konverteringen misslyckades. Detta är inte så bra ifall användaren t.ex. skulle skriva in sin ålder. Vi kan göra programmet ännu bättre genom att låta användaren få försöka skriva in ett tal igen om konverteringen misslyckades, detta kan fortsätta ändå till användaren lyckas skriva in ett giltigt heltal. I nästa version av programmet så åstadkommer vi detta med en while-loop som körs så länge som konverteringen misslyckas.

Console.WriteLine("Skriv in ett heltal");
int heltal;
while (int.TryParse(Console.ReadLine(), out heltal) == false)
{
    Console.WriteLine("Du skrev inte in ett heltal. Försök igen.");                
}

Console.WriteLine($"Du skrev in talet {heltal}");

Console.ReadKey();

Varje gång while-loopens villkor ska undersökas så får användaren försöka skriva in ett nytt tal eftersom det finns en Console.ReadLine där. Loopen kommer inte att köras längre när konverteringen har lyckats och då kan vi vara säkra på att heltal innehåller ett giltigt heltal som användaren har skrivit in.

Detta sätt att konvertera text till tal fungerar väldigt bra, men det blir väldigt mycket att skriva varje gång som vi vill läsa in ett heltal vilket vi ofta vill göra. För att slippa skriva all denna kod varje gång så flyttar vi koden som läser in och konverterar texten till ett heltal till en egen metod som vi kallar ReadInt. Med hjälp av denna metod kan vi enkelt läsa in heltal från användaren och vara säkra på att programmet inte kommer att krascha.

static void Main(string[] args)
{
    Console.WriteLine("Skriv in ett heltal");
    int heltal1 = ReadInt();
    Console.WriteLine("Skriv in ett heltal till");
    int heltal2 = ReadInt();
    Console.WriteLine("Skriv in ett sista heltal");
    int heltal3 = ReadInt();

    Console.WriteLine($"Du skrev in talen {heltal1}, {heltal2} och {heltal3}");

    Console.ReadKey();
}

static int ReadInt()
{
    int heltal;
    while (int.TryParse(Console.ReadLine(), out heltal) == false)
    {
        Console.WriteLine("Du skrev inte in ett heltal. Försök igen.");
    }
    return heltal;
}

Från och med nu bör du alltid använda en ReadInt-metod som du har skrivit själv när du ska läsa in heltal från användaren istället för int.Parse. Du behöver såklart inte skriva om metoden varje gång, det lättaste är att kopiera denna metod och lägga in den i alla nya program du gör. I bokens exempel kommer int.Parse att fortsätta användas eftersom detta tar mindre plats att skriva än att kopiera in en ReadInt-metod, men du bör som sagt använda ReadInt varje gång som int.Parse används.

Uppgift 7.9

Gör en metod som heter ReadDouble som fungerar på samma sätt som ReadInt fast för decimaltal istället för heltal. Kopiera denna metod till alla nya program där du vill läsa in ett decimaltal från användaren.

Lösningsförslag 7.9

Uppgift 7.10

Öppna ett av dina gamla program, förslagsvis ett menyprogram eller en projektuppgift, och kopiera in dina ReadInt och ReadDouble-metoder. Skriv om programmet så att det använder dessa metoder istället för int.Parse och double.Parse.

Metodkommentarer

Ända sedan bokens början har du fått se hur man kan skriva kommentarer i sin kod, och kommentarer har använts i bokens exempel för att förklara hur vissa delar av exempelprogrammen fungerar. Vi ska nu se hur man kan skriva en särskild sorts kommentarer till de metoder som man själv skrivit och hur de sedan används av Visual Studio för att förklara metoder när man anropar dem.

För att skriva metodkommentarer så placerar du textmarkören på raden ovanför en metod och skriver därefter 3 stycken snedstreck, ///. Visual Studio fyller då i skalet för en metodkommentar som man sedan får fylla i själv. Figuren nedan visar var du ska fylla i snedstrecken, det sista snedstrecket är inte skrivet än.

Metodkommentarer

I exemplet nedan så har metoden Addera som vi studerade tidigare i kapitlet fått en metodkommentar.

static void Main(string[] args)
{
    int summa = Addera(1, 3);
    Console.WriteLine(summa);

    Console.ReadKey();
}

/// <summary>
/// Adderar två heltal
/// </summary>
/// <param name="tal1">Det ena talet</param>
/// <param name="tal2">Det andra talet</param>
/// <returns>Summan av talen</returns>
static int Addera(int tal1, int tal2)
{
    return tal1 + tal2;
}

I metodkommentaren så har vi skrivit en sammanfattning som kort beskriver vad metoden gör, en kort beskrivning av varje parameter samt en beskrivning av vad metoden returnerar. Förutom att det är bra att kommentera sina metoder så att snabbt går att läsa en beskrivning av vad de gör så får man dessutom upp texten i metodkommentarerna när man anropar dem vilket du kan se i nästa figur.

Visning av metodkommentarer

Från och med nu bör du alltid skriva med metodkommentarer till de metoder du gör där det inte känns uppenbart vad metoden gör. Det viktigaste när man skriver en metod är att metoden har ett namn som tydligt beskriver vad den gör, men detta räcker inte alltid för att man ska förstå vad man ska skicka som argument till metoden. En bra tumregel är att det ska vara lätt för en kompis som är lika duktig som dig att förstå ditt program och dina metoder ganska snabbt genom att läsa koden. Om din kod kräver kommentarer för att det skulle gå snabbt för kompisen att göra detta så bör du kommentera din kod och då är metodkommentarer väldigt bra att använda.

Tillräckligt med kommentarer?

Om ditt program innehåller metoder med tydliga namn och metodkommentarer så räcker detta ofta för att ett program ska vara välkommenterat. Vissa metoder kan ibland ha väldigt krånglig kod som kan behöva ytterligare kommentarer inuti metoden men metodkommentarer brukar för det mesta räcka.

Uppgift 7.11

Skriv metodkommentarer till metoderna i ett valfritt menyprogram som du tidigare har gjort.

Uppgift 7.12

Skriv metodkommentarer till dina ReadInt och ReadDouble-metoder som du använder för att läsa in heltal och decimaltal från användaren. Se till att alltid kopiera med dessa kommentarer när du kopierar metoderna till dina nya program.

Lösningsförslag 7.12

Klassvariabler och lokala variabler

Alla variabler och all kod som vi hittills har skrivit har alltid skrivits inuti en metod, antingen Main-metoden eller någon annan metod som vi har skapat själva. Det är dock inte nödvändigt för en variabel att deklareras inuti en metod, det går att skapa den utanför alla programmets metoder men inuti class-blocket som ligger utanför metoderna.

När man deklarerar en variabel inuti en metod, så som vi har gjort varje gång hittills, så kallas variabeln för en lokal variabel. En lokal variabel är bara tillgänglig inuti den metod som den är deklarerad i, den kan alltså inte användas i någon annan metod. När man istället deklarerar en variabel utanför programmets metoder, en klassvariabel, så är den tillgänglig för alla metoder i programmet.

För att deklarera en klassvariabel så skriver du den alltså utanför programmets metoder men inuti programmets class-block. Det är inte nödvändigt att placera klassvariablerna högst upp inuti class-blocket men man gör ofta det för att det ska vara lätt att hitta dem i koden. Du behöver lägga till ordet static innan du deklarerar variabeln, precis som du gör när du deklarerar en egen metod.

static string klassvariabel = "Detta är en klassvariabel";

static void Main(string[] args)
{
    string lokalVariabel = "Detta är en lokal variabel";
    string egenVariabel = "Detta är också en lokal variabel";
    Console.WriteLine(lokalVariabel);
    Console.WriteLine(egenVariabel);
    Console.WriteLine(klassvariabel);

    SkrivUtInfo();

    Console.ReadKey();
}

/// <summary>
/// Skriver ut värdet av två variabler
/// </summary>
static void SkrivUtInfo()
{
    string lokalVariabel = "Här är en annan lokal variabel";
    Console.WriteLine(lokalVariabel);
    Console.WriteLine(klassvariabel);
} 

I detta program så kan strängen klassvariabel användas från båda programmets metoder, däremot så kan inte någon av de lokala variablerna i Main-metoden användas i SkrivUtInfo-metoden. Varje metod kommer bara åt de programmets klassvariabler och de variabler som är deklarerade inuti just den metoden. Det spelar ingen roll att man skapar en ny lokal variabel i SkrivUtInfo som har samma namn som en av de lokala variablerna i Main-metoden, de kommer ändå att vara olika variabler i datorns minne trots att de har samma namn.

En variabeltyp som du som regel alltid bör skapa som en klassvariabel är Random-variabler som används för att slumpa fram tal. På grund av hur Random-variabler fungerar så kommer två olika Random-variabler som skapas samtidigt när programmet körs att generera precis samma slumptal vilket man oftast inte vill. Genom att bara låta varje program ha en Random-variabel som är en klassvariabel så slipper man detta problem, du kan se i nästa exempel hur det kan gå till.

static Random slump = new Random();

static void Main(string[] args)
{
    Console.WriteLine($"Här är ett tärningskast: {KastaTärning()}");
    Console.WriteLine($"Ett tärningskast till: {KastaTärning()}");
    Console.WriteLine($"Slumpat tal mellan 1 och 10: {slump.Next(1, 11)}");

    Console.ReadKey();
}

/// <summary>
/// Kastar en sexsidig tärning
/// </summary>
/// <returns>Resultatet av tärningskastet</returns>
static int KastaTärning()
{
    return slump.Next(1, 7);
}

Klassvariabler är vanliga i större program än i små, t.ex. kommer vi att se mer av dem i kommande projektuppgifter. De används ofta för variabler som innehåller inställningar för programmet. Däremot så ska man inte helt plötsligt ersätta alla lokala variabler i ett program med klassvariabler. Desto mer ett program arbetar med klassvariabler, desto svårare kan det bli att få en överblick av vad programmets metoder gör eftersom man kommer att behöva hålla en större del av programmets kod i huvudet på en gång. Det blir också ofta svårare att hitta fel i program som använder sig mycket av klassvariabler än de som inte gör det.

Krascher med klassvariabler

Ibland kan ett program krascha om man inte har gett ett värde till en klassvariabel och sedan försöker använda den. Du kommer få lära dig mer om varför detta händer i nästa kapitel.

En bra princip är att i första hand försöka skapa metoder som inte behöver läsa eller spara information i klassvariabler, försök istället att använda parametrar och return i dina metoder för att åstadkomma det du vill göra.

Uppgift 7.13

Skapa ett program som innehåller en metod som heter SlumpaÅrtal som ska slumpa fram ett årtal från 1900-talet, alltså från 1900 till 1999. Programmet ska också innehålla en metod som heter SlumpaMånad som slumpar ett tal från 1 till 12.

Lösningstips 7.13

Tänk på att göra Random-variabeln till en klassvariabel!

Lösningsförslag 7.13

Uppgift 7.14

Du ska skriva ett program som slumpar en position på ett schackbräde. En schackposition består av en kolumn som betecknas med en liten bokstav från a till h samt en rad som betecknas med ett tal från 1 till 8. Några exempel på schackpositioner är a3, f1 och h5. Ditt program ska använda sig av följande metoder för att slumpa schackpositionen:

  • SlumpaRad som returnerar ett tal från 1 till 8.
  • SlumpaKolumn som returnerar en bokstav från a till h.
  • SlumpaPosition som returnerar en hel schackposition, t.ex. a4. Denna metod ska använda sig av de båda andra metoderna när den anropas.
Lösningstips 7.14

Använd strängen string bokstäver = "abcdefgh" för att slumpa bokstaven för kolumnen.

Lösningsförslag 7.14

Överlagring – Flera metoder med samma namn

Det är möjligt att låta två metoder i samma program ha samma namn, detta kallas att överlagra metoden. För att överlagra en metod så måste metodernas parametrar skilja sig åt, om den ena metoden t.ex. har två strängar som parametrar så skulle den andra kunna t.ex. kunna ha 3 strängar eller 1 sträng och ett decimaltal. Det går att ha hur många överlagringar av samma metod som man vill så länge inte någon har samma uppsättning parametrar som någon annan.

I exemplet nedan så är metoden SkrivVertikalt överlagrad, den har en överlagring som har en sträng som parameter och en som har en int som parameter. Överlagringen som har en int som parameter anropar den andra överlagringen efter att ha omvandlat parametern vertikaltTal till en sträng. Tack vare denna överlagring så behöver man inte själv omvandla ett tal till en sträng när man vill skriva ut ett tal vertikalt, överlagringen sköter det åt oss.

static void Main(string[] args)
{
    SkrivVertikalt("Hej");
    SkrivVertikalt(531);

    Console.ReadKey();
}

/// <summary>
/// Skriver ut ett meddelande vertikalt, ett tecken per rad
/// </summary>
/// <param name="meddelande">Texten som ska skrivas vertikalt</param>
static void SkrivVertikalt(string meddelande)
{
    for (int i = 0; i < meddelande.Length; i++)
    {
        Console.WriteLine(meddelande[i]);
    }
}

/// <summary>
/// Skriver ut ett tal vertikalt, en siffra per rad
/// </summary>
/// <param name="vertikaltTal">Talet som ska skrivas vertikalt</param>
static void SkrivVertikalt(int vertikaltTal)
{
    SkrivVertikalt(vertikaltTal.ToString());
}

Det är möjligt att låta två överlagringar returnera olika typer av variabler, men vilken variabeltyp en överlagring returnerar kan inte vara det enda som skiljer den från andra överlagringar, parametrarna måste också vara olika.

Du har använt överlagringar av många metoder tidigare, varje gång du har haft möjlighet att ange olika variabeltyper i en metod eller olika antal parametrar så är det tack vare överlagring. Exempel på överlagrade metoder som du har använt är IndexOf, Substring, Next som används när du slumpar heltal samt Console.Writeline.

Uppgift 7.15

Skapa ett program som innehåller tre metoder som alla heter SkrivBaklänges. En av dem ska användas för att skriva en sträng baklänges, en av dem ska skriva ut en int baklänges och den sista ska skriva ut en double baklänges.

Lösningstips 7.15

Metoderna för int och double kan anropa metoden som har en string som parameter om du först omvandlar talet till en sträng. Om du någon gång vill ändra hur metoderna fungerar så behöver du (antagligen) bara ändra i metoden som har en string som parameter och man ska alltid sträva efter att skapa program som är lätta att ändra.

Lösningsförslag 7.15

Valbara och namngivna argument

Vi ska undersöka några fler alternativ man har när man skapar egna metoder. En möjlighet man har är att göra ett eller flera argument valbara genom att ge ett standardvärde till en parameter. I exemplet nedan så har parametern sidlängd getts ett standardvärde vilket innebär att man inte måste ange ett argument när man anropar metoden. Om man inte anger något argument så kommer parametern i detta fall få värdet 5.

static void Main(string[] args)
{
    RitaKvadrat();
    Console.ReadKey();
    RitaKvadrat(3);
    Console.ReadKey();
    RitaKvadrat();

    Console.ReadKey();
}

/// <summary>
/// Ritar en kvadrat bestående av X
/// </summary>
/// <param name="sidlängd">Kvadratens sidlängd</param>
static void RitaKvadrat(int sidlängd = 5)
{
    for (int i = 0; i < sidlängd; i++)
    {
        for (int j = 0; j < sidlängd; j++)
        {
            Console.Write("X");
        }
        Console.WriteLine();
    }
}

En metod kan ha både parametrar med och utan standardvärden, men de parametrar som har standardvärden måste komma sist av metodens parametrar. Fortsättningsvis i denna bok så kommer de flesta metoder inte att skrivas med parametrar med standardvärden, ibland stöter man dock på en metod som man inte har skrivit själv som anropas med ett valbart argument och då behöver man känna till vad detta är för något. Ibland så kan det också vara så att det känns lämpligt att låta en parameter ha ett standardvärde och då ska man såklart ge den det.

En annan möjlighet man kan använda när man anropar en metod är att namnge argumenten som metoden anropas med. När man gör detta så behöver man inte skriva argumenten i samma ordning som metodens parametrar är skrivna.

static void Main(string[] args)
{
    SkrivUpprepadText(text: "Ett ", antalUpprepningar: 5);
    SkrivUpprepadText(antalUpprepningar: 3, text: "Två ");

    Console.ReadKey();
}

/// <summary>
/// Skriver en sträng ett upprepat antal gånger, avslutar med radbrytning
/// </summary>
/// <param name="text">Texten som ska upprepas</param>
/// <param name="antalUpprepningar">Antalet gånger texten ska upprepas</param>
static void SkrivUpprepadText(string text, int antalUpprepningar)
{
    for (int i = 0; i < antalUpprepningar; i++)
    {
        Console.Write(text);
    }
    Console.WriteLine();
}

Det är vanligt att anropa metoder på det sätt som vi tidigare har gjort, d.v.s. utan att använda namngivna argument. Möjligheten finns att använda dem om man vill även om vi i denna bok kommer fortsätta att anropa metoder med argumenten i samma ordning som metodens parametrar.

Uppgift 7.16

Skapa ett program med en metod som heter SkrivVertikalt. Metoden ska ha två parametrar, en parameter ska vara en sträng som ska skrivas ut vertikalt och den andra ska en valbar parameter som är en int som heter antalTommaRader. Parametern antalTommaRader ska ange hur många tomma rader som ska ritas ut mellan varje tecken i metodens sträng-parameter när den skrivs ut vertikalt. Om man inte anger den valbara parametern så ska det inte ritas ut några extra tomma rader mellan varje tecken.

Lösningstips 7.16

Det är lämpligt att använda nästlade loopar i denna uppgift.

Lösningsförslag 7.16

Uppgift 7.17

Bygg ut programmet i den föregående uppgiften så att metoden har ytterligare en valbar int-parameter som heter stegÅtHöger. Denna parameter anger hur många steg åt höger det vertikala meddelandet ska flyttas när det skrivs ut. Om användaren inte anger denna parameter så ska meddelandet skrivas ut längst till vänster i programfönstret.

Lösningsförslag 7.17

Blandade uppgifter till kapitel 7

Uppgift 7.18

Skapa ett program som innehåller metoden

static int Addera(int tal1, int tal2, int tal3)

Metoden ska addera och returnera summan av de tre talen.

Lösningsförslag 7.18

Uppgift 7.19

Skapa ett program som innehåller metoden

static int Störst(int tal1, int tal2)

Metoden ska returnera det största talet av tal1 och tal2. Om båda talen är lika stora så returnerar du det värde som talen har.

Lösningsförslag 7.19

Uppgift 7.20

Skapa ett menyprogram med följande alternativ

1. Addera tre tal
2. Största talet av två tal
3. Avsluta programmet

Programmet ska vara uppdelat i metoder, det ska finnas en metod för varje menyalternativ. Programmet ska innehålla två metoder, MenyvalAddera och MenyvalStörstaTalet, som hanterar de båda menyalternativen. Dessa metoder ska i sin tur använda sig av metoderna Addera och Störst som du skrev i de två föregående uppgifterna. Du kan kopiera dessa metoder från dina tidigare program till ditt nya program.

Lösningsförslag 7.20

Uppgift 7.21

Skapa ett program som innehåller en metod som kan användas för att beräkna potenser, t.ex. 32 eller 6,24. Du kan förutsätta att potensens exponent är ett positivt heltal.

Lösningstips 7.21

Börja metoden med double svar = 1, det är denna variabel du ska returnera längst ner i metoden. Använd en loop för att svar ska få rätt värde. Du kan ge svar ett startvärde som är detsamma som potensens bas om du vill istället.

Lösningsförslag 7.21

Uppgift 7.22

Skapa ett program som innehåller en metod som heter Längst. Metoden ska returnera den längsta av de två strängar som metoden ska ha som parametrar. Metoden ska överlagras så att den också kan användas för att returnera det längsta av två heltal. Om båda argumenten är lika långa så ska metoden returnera det första av dem.

Lösningsförslag 7.22

Uppgift 7.23

Skapa ett program med en metod som undersöker om ett visst heltal är ett primtal eller inte. Ett primtal är ett tal som är större än 1 som bara är delbart med sig själv och 1.

Lösningstips 7.23

Försök att undersöka om det finns någon tal som heltalet är delbart med från 2 till heltal - 1. Om det är delbart med något av dessa tal kan du genast returnera false. Om det inte är det så kan du retunera true.

Lösningsförslag 7.23

Uppgift 7.24

Skapa ett program som frågar användaren efter ett heltal. Programmet ska sedan, med hjälp av en metod, beräkna hur många primtal som är mindre än det heltal som användaren skrev in. Metoden som beräknar antalet primtal ska använda sig av metoden du skrev i den föregående uppgiften som undersöker om ett tal är ett primtal eller inte.

Lösningsförslag 7.24

Uppgift 7.25

Skapa ett menyprogram med följande alternativ.

1. Är n ett primtal?
2. Antalet primtal mindre än n
3. Avsluta programmet
I alternativ 1 så ska användaren få skriva in ett heltal, programmet ska berätta för användaren om det är ett primtal eller inte. I alternativ 2 så ska användaren få skriva in ett heltal och programmet ska berätta antalet primtal som är mindre än det inskrivna talet. Programmet ska vara uppdelat i metoder och det ska dessutom använda sig av de metoder du har skrivit i de föregående uppgifterna.

Lösningsförslag 7.25

Uppgift 7.26

Skapa ett program som innehåller en metod som tar en sträng som parameter och skriver ut strängen fast vartannat ord i strängen ska skrivas ut med enbart stora bokstäver och vartannat ord med enbart små. Om metoden anropas med argumentet ” Detta är ett exempel. Texten ändras när den skrivs ut.” så ska utskriften bli ”DETTA är ETT exempel. TEXTEN ändras NÄR den SKRIVS ut.”

Lösningstips 7.26

Utnyttja metoden Split som du sett i ett tidigare kapitel.

Lösningsförslag 7.26

Uppgift 7.27

Skapa en metod som du kallar för Substring som ska ha tre parametrar, en sträng, ett startindex och ett slutindex. Metoden ska returnera en Substring från sträng-parametern som börjar på startindexet och slutar på slutindexet. Den som anropar metoden ska inte behöva ange start eller slutindex om hen inte vill. Standardvärdet för startindex ska vara 0, standardvärdet för slutindex ska vara -1 vilket din metod ska tolka som att strängens sista index blir slutindex (du kan inte skriva text.Length som standardvärde).

Anropa metoden 3 gånger i ditt program. En gång ska du bara ange startindex, en gång bara slutindex och en gång både start- och slutindex.

Lösningstips 7.27

Utnyttja den vanliga Substring-metoden i din egen metod.

Lösningsförslag 7.27

Uppgift 7.28

Skapa ett program med en metod som heter RitaKvadrat som ritar ut en kvadrat av valfri storlek bestående av ett valfritt tecken. Den som anropar metoden ska kunna ange hur stort mellanrummet ska vara mellan tecknen som skrivs ut, detta ska påverka mellanrummet både i x- och y-led. Om mellanrumsstorleken inte anges vid anropet av metoden så ska den vara 0.

Om metoden anropas med RitaKvadrat(4, 'A') så ska följande kvadrat ritas ut:

AAAA
AAAA
AAAA
AAAA

Om metoden anropas med RitaKvadrat(3, 'B', 2) så ska följande kvadrat ritas ut:

B  B  B


B  B  B


B  B  B

Lösningsförslag 7.28

Uppgift 7.29

Skapa ett program som innehåller en metod som kan rita en triangel där användaren får bestämma triangelns höjd. Triangeln ska se ut så här om höjden är 3:

  *
 ***
*****
Om höjden är 4 ska den istället se ut så här:
   *
  ***
 *****
*******

Lösningsförslag 7.29

Kommentarer