13 Metoder (funktioner)

Hittills har vi skrivit all vår källkod direkt i det fördefinierade kodblocket Main men detta blir ganska snabbt lite stökigt, särskilt när våra program börja växa lite i antalet kodrader.

Ett sätt att dela upp programmet i mer hanterbara delar är att använda metoder (ibland benämns metoder också som funktioner, men utifrån hur programspråket C# är uppbygt är det korrekta namnet egentligen metoder).

Du har använt massvis med metoder i dina program hittills, t.ex. är Split en metod som finns för strängar eller Round en metod som finns hos Math-klassen.

13.1 Metoddefinition

En metoddefinition har två delar - ett metodhuvud och en metodkropp. Metodhuvudet kallas också ibland för metodens signatur. Följande är ett exempel på en (väldigt enkel) metod:

int Addera(int a, int b)
{
    int summa = a + b;
    return summa;
}

På första raden ser vi metoden signatur och inom måsvingarna {} ser vi metodens kropp. På signaturraden betyder de olika nyckelorden följande:

  • int: Metodens returtyp. Det som metoden i slutändan svarar med skall vara av denna datatyp. I detta fall adderar metoden två heltal - alltså ska svaret vara ett heltal.
  • Addera: Metodens namn. Detta namn bestämmer du som programmerare själv men ska vara så beskrivande som möjligt, precis som med variabelnamn. Metodnamn börjar alltid med stor bokstav. Består namnet av flera ord används samma princip som för variabelnamn (t.ex: AdderaTvaTal). Metodnamn är jämförbart med \(f\) i det matematiska uttryckssättet \(f(x)\).
  • int a: Datatyp och variabelnamn för den första parametern. Parameter är ett namn på den information som skickas med in i en metod. Jämför med \(x\) i \(f(x)\).
  • int b: Datatyp och variabelnamn för den andra parametern. Jämför med \(y\) i \(f(x,y)\).

OBS! Antalet parametrar måste ej vara två, det kan vara 0 eller flera. Om en metod har 0 parametrar lämnas två tomma paranteser direkt efter namnet, annars separeras varje parameter med ett kommatecken.

För att få en metod att fungera korrekt i de konsollprogram vi skriver måste en sak till med i vår signatur - nämligen static. Eftersom att vi skriver all vår kod i metoden Main som i sig är static måste alla metoder vi använder i Main i sin tur vara static - en regel i C# som man helt enkelt får lära sig. Vi får då följande kod:

static int Addera(int a, int b)
{
    int summa = a + b;
    return summa;
}

Inuti metodens kropp kan du programmera helt enligt tidigare principer. Märk dock noga att så fort programmet stöter på nyckelordet return så kommer metoden att returnera (avslutas) och inte köra resten av koden som eventuellt kan finnas under. Första bästa return innebär alltså att metodanropet anses vara klart!

Överkurs: Det som static egentligen betyder är att det inte behövs ett objekt för att komma åt metoden, man kan arbeta direkt mot klassen. Jämför t.ex. Math.Round() med metoden vi använder för att få fram ett slumptal Next(). För att komma åt Next() måste vi första skapa en new Random() men med Round() kan vi köra Math.Round() direkt utan att använda new. Mycket mer (och begripligare) om detta om du väljer att läsa kursen Programmering 2 eller någon kurs i objektorienterad programmering på högskola/universitet.

13.2 Att anropa en metod

När metoden väl är skapad (huvudet + kroppen) skall vi givetvis använda den i vårt huvudprogram - annars är den ju inte till någon nytta. Vår källkod kan då se ut såhär:

static void Main(string[] args)
{
    int resultat = Addera(5, 7);
    Console.WriteLine(resultat);
}

static int Addera(int a, int b)
{
    int summa = a + b;
    return summa;
}

Anropet till funktionen ser alltså ut såhär: Addera(5, 7). Värdena 5 och 7 i detta fall kallas argument till metoden (det som skickas med). Svaret ifrån detta anrop (resultatet av koden i metoden) lagras i variabeln resultat. Eftersom att returtypen på Addera är int måste även datatypen på variabeln som tar emot resultatet vara int.

Det som händer vi körning är att när Addera(5, 7) anropas så får a värdet 5 och b värdet 7 i metoddefinitionen, beräkningen genomförs, svaret returneras och lagras sedan i variabeln resultat.

Ett metodanrop med return-sats.

Om samma metod anropas en gång till så har värdena på a, b och summa raderats. Det är en ny, färsk variant av metoden som körs andra gången.

static void Main(string[] args)
{
    int resultat = Addera(5, 7);
    Console.WriteLine(resultat); // 12
    resultat = Addera(1, 2);
    Console.WriteLine(resultat); // 3
}

static int Addera(int a, int b)
{
    int summa = a + b;
    return summa;
}

13.2.1 Lokala variabler

I exemplet ovan är summa ett exempel på en så kallad lokal variabel. Den existerar endast inuti metoden Addera och är ej åtkomlig utanför. När metoden har körts klart raderas värdet som lagrats i summa och minnet för variabeln frigjörs. Observera dock att värdet som returnerats (och lagrats i resultat) inte raderas. I tidigare nämnda exempel är 12 resultatet av att anropa Addera(5,7), den 12:an finns i huvudprogrammet och lagras i resultat. Metoden Addera har dock inget minne av varken 5, 7 eller 12 efter en genomkörning.

13.2.2 Kopior av värden

Parametrarna i en metodsignaturen får kopior av de värden som skickas med, det är med andra ord inte variabeln i sig som skickas iväg till en metod. Låt oss studera nedanstående exempel:

static void Main(string[] args)
{
    int a = 5;
    Console.WriteLine(a); // 5

    int b = AdderaTre(a);
    Console.WriteLine(a); // 5
    Console.WriteLine(b); // 8
}

static int AdderaTre(int x)
{
    x = x + 3;
    return x;
}

Trots att x tilldelas nytt värde i AdderaTre så påverkas ej variabeln a, eftersom det är en kopia av a som skickas till metoden.

13.3 Returtyper

Alla datatyper vi jobbat med kan användas som returtyper (int, double, bool, char, string och alla arrayvarianter…). Vi lägger även till en ny till listan - void (tomrum på engelska). Denna returtyp används om metoden inte ska returnera något alls, något som bland annat är vanligt om metoden t.ex. endast ska göra en utskrift av något. Något resultat skickas då alltså inte tillbaka till huvudprogrammet (eller där anropet skedde).

En metod som tar en int-array och endast gör en utskrift av denna kan se ut såhär:

static void SkrivUtArray(int[] tal)
{
    for (int i = 0; i < tal.Length; i++)
    {
        Console.Write(tal[i] + " ");
    }
}

När returtypen void används ska alltså inte nyckelordet return användas i metodkroppen. När en metod inte returnerar något kan inte heller resultatet av anropet lagras i en variabel (eftersom att det inte finns något resultat). Då ser anropet helt enkelt ut såhär:

int[] matningar = { 5, 7, -1, 32, 11, 17 };
SkrivUtArray(matningar);

13.4 Array som parameter

Ett specialfall vad gäller parametrar är arrayer. Eftersom att de som designat programspråket C# har haft (bland annat) prestanda i åtanke så blir en array-parameter inte en kopia som de andra parametrarna. Detta på grund av att en array mycket väl kan innehålla en stor datamängd och att göra en kopia på en en sådan är väldigt resurskrävande. Istället blir arrayen i detta fall en så kallad referensparameter. Parameternamnet i metoden pekar på den “riktiga” variabeln i huvudprogrammet. Med andra ord blir en ändring av arrayens värden inuti metoden även en ändring i huvudprogrammet (även om vi inte returnerar något) - det är samma array! Koden nedan är exempel på detta:

static void Main(string[] args)
{
    int[] minArray = { 1, 2, 3, 4, 5 };

    // Före
    for (int i = 0; i < minArray.Length; i++)
    {
        Console.Write(minArray[i] + " ");
    }

    Console.WriteLine();
    MultipliceraMedFem(minArray); // Anropa metoden

    // Efter
    for (int i = 0; i < minArray.Length; i++)
    {
        Console.Write(minArray[i] + " ");
    }
}

static void MultipliceraMedFem(int[] minParameter)
{
    for (int i = 0; i < minParameter.Length; i++)
    {
        minParameter[i] *= 5;
    }
}

Resultatet av koden ovan är att första utskriften blir 1 2 3 4 5 och den andra blir 5 10 15 20 25, trots att det är parametern man jobbar med i metoden MultipliceraMedFem och ingen retur-sats finns.

13.5 Överlagrade metoder

En metodsignatur måste vara unik men det betyder inte att två metoder inte kan ha samma namn. Bara de inte har exakt samma parametrar. Signaturen utgörs av hela metodhuvudet, inklusive returtyp och parametrar. Det är t.ex. helt ok att i ett och samma program ha två metoder enligt nedanstående:

public static void Main()
{
    int[] tal = { 1, 2 };
    Console.WriteLine(Addera(tal));
    Console.WriteLine(Addera(1, 2));
}

static int Addera(int a, int b)
{
    return a + b;
}

static int Addera(int[] array)
{
    int summa = 0;
    for (int i = 0; i < array.Length; i++)
    {
        summa += array[i];
    }
    return summa;
}

Metoder med samma namn men olika signatur kallas överlagrade metoder och det är dessa du kan bläddra mellan (med piltangenterna) i Visual Studio när du börjar skriva en parantes efter metodnamnet i ett anrop.

Bläddra bland överlagrade metoder.

13.6 Gemensamma variabler

Om en variabel behöver vara gemensam för flera olika metoder (dvs. åtkomlig från vilken metod som helst) kan man deklarera den precis ovanför Main-metoden. Denna variabeldeklaration ska föregås av static av samma anledning som angavs tidigare. Ett minimalt exempel kan se ut såhär:

static int a;

public static void Main()
{
    a = 12;
    SkrivUtA();
}

static void SkrivUtA()
{
    Console.WriteLine(a);
}

13.6.1 Övningar

  1. Skriv ett program som låter användaren mata in ett heltal och sedan svarar med kvadraten på det talet. Beräkningen skall göras i en egen metod som returnerar kvadraten.

  2. Redigera i ditt program från uppgift 38 så att användaren får bestämma både bas och exponent. Programmet svarar sedan med värdet av potensen (som beräknas i en egen metod).

  3. Skapa ett program där du matar in radien på en cirkel. Programmet skall sedan räkna ut både area och omkrets på cirkeln. Till beräkningarna skall du använda dig av metoder, en för areaberäkningen och en för beräkningen av omkretsen. Runda av till två decimaler med Math.Round(). Area och omkrets av en cirkel.

  4. Skriv ett program där du definierar en heltalsarray. Skapa sedan en egen metod som summerar alla talen i arrayen. Metoden skall returnera ett heltal. Skriv ut summan.

  5. Skapa en tecken-array med de sex första bokstäverna i alfabetet som gemener. Skapa sedan en metod som tar emot en char-array och byter från gemener till versaler (studera Unicode-tabellen om du glömt av hur man gör). Metoden skall ha returtypen void. Skriv ut samtliga tecken i arrayen med hjälp av ännu en metod av returtypen void.

  6. Skriv ett program där tre endimensionella heltalsarrayer av samma storlek skapas. Fyll två av dessa med slumpade heltal mellan 1 och 10.

    1. Skriv sedan en metod som tar emot tre heltalsarrayer a, b och c. Metoden skall beräkna summan för respektive element i a och b för att sedan lagra detta i c (den tomma arrayen). Metoden skall vara generel på så vis att den fungerar för vilken arraystorlek som helst (i en dimension).

    2. Skriv sedan till metod som tar emot en heltalsarray och skriver ut samtliga av dessa tal till skärmen på en och samma rad. Metoden skall inte returnera något (void).

    3. Använd dina metoder för att beräkna summa och sedan göra utskrift av samtliga tre arrayer.

  7. Gör ett återbesök på det första praktiska provet på Moodle: moodle.teed.se. Ta en titt på del 2 av temperaturuppgiften (då man ska kunna mata in 3 temperaturer och beräkna medel). Lös denna med dina nya kunskaper om arrayer och metoder!

  8. I rollspel brukar tärningsslag beskrivas som “1T6” vilket betyder att en tärning med 6 sidor skall kastas en gång. Ibland skall en tärning kastas flera gånger t.ex. “3T6” vilket betyder 3 kast med en vanlig tärning. Skriv ett program där du skapar en metod KastaTarning som har en parameter för hur många gånger tärningen skall kastas. Resultatet skall returneras. Vi förutsätter en vanlig 6-sidig tärning.

  9. Bygg vidare på övning 45 och skapa en överlagrad metod till KastaTarning. Den nya varianten skall ha både antalet sidor på tärningen som skall kastas samt antalet gånger tärningen skall kastas som parametrar. Dvs. du skall kunna slå “3T6” eller “2T20” om du så vill med att anropa KastaTarning(6, 3) eller KastaTarning(20, 2). Resultatet skall givetvis returneras från metoden.

  10. Bygg vidare på övning 46 och skapa ytterligare en överlagrad metod till KastaTarning. Denna gång skall parametern vara av typen string. Du skall nämligen direkt kunna tolka texten “3T6”, “2T4” eller “1T20” som ett tärningskast och returnera resultatet. Här krävs lite stränghantering, kolla i tidigare kapitel om du är osäker!