Gå til indholdet

Metoder

Metoder, som i nogle sprog også benævnes funktioner, kan gøre koden mere genbrugelig og overskuelig, og giver mulighed for at skabe blokke af instruktioner som kan kaldes fra forskellige steder i koden. En metode kan enten være en metode, som blot afvikler instruktioner uden en returværdi, eller en samling af instruktioner, som returnerer en værdi ligesom en matematisk funktion. Yderligere kan en metode kaldes med en samling værdier (kaldet argumenter), som kan benyttes i metoden.

Information til undervisere

Sværeste her er at tale om metoder uden at tale for meget OOP eller static-kodeordet. Brug evt den simple konsol applikation

// Husk - metoder i starten af filen
void SkrivTal(int tal)
{
    Console.WriteLine($"Tallet er {tal}"); 
}

SkrivTal(1);

men problemet er at nogle feature ikke er tilgængelige i denne form (herunder overloades). Jeg synes du blot skal forklare at alt i C# er objektorienteret, og selvom man (endnu) ikke forstår hvorfor så skal alt i klasser, og klasser skal der laves en instans af eller den kan bestå af statiske medlemmer som kan kaldes direkte på klassen. Så brug eksempelvis denne som skabelon, og lad kursisten lege med forskellige metoder gennem MinKlasse. Men prøv at lade være med at gå for meget op i public og static - lige nu skal de bare skrive noget kode. Metoderne kan også bare placeres direkte i Program-klassen.

MinKlasse.SkrivTal(1);

public static class MinKlasse
{
    public static void SkrivTal(int tal)
    {
        Console.WriteLine($"Tallet er {tal}");
    }
}

Expression body methods er ikke for begyndere, og det samme gælder attributter - mem måske bør det nævnes

Hvorfor benytte metoder

Som eksempel på brug af metoder for at gøre koden både nemmere at genbruge og læse, kan du forstille dig en opgave, hvor du skal tælle fra 1 til 5, udskrive værdien, og gentage dette tre gange. Det kunne jo kodes således:

for (int i = 1; i < 6; i++)
    Console.WriteLine(i);

for (int i = 1; i < 6; i++)
    Console.WriteLine(i);

for (int i = 1; i < 6; i++)
    Console.WriteLine(i);

men gentaget kode er roden til alt ondt – hvad nu hvis opgaven i fremtiden ændres, så du skal tælle til 10. Så skal du ændre koden tre steder.

Måske kunne det i stedet kodes som:

for (int x = 0; x < 3; x++)
    for (int i = 1; i < 6; i++)
        Console.WriteLine(i);

Det ser noget bedre ud og giver samme resultat. Men det kan ikke genbruges uden at kopiere koden – og så er vi tilbage i gentaget kode.

Du kunne også rode dig ud i noget goto-fnidder:

int programLinjeTæller = 0;
start:
if (programLinjeTæller < 3)
    goto tæl;
else
    goto slut;
tæl:
for (int i = 1; i < 6; i++)
    Console.WriteLine(i);
programLinjeTæller++;
goto start;
slut:

Nu kan koden i princippet genbruges, men sikke en gang rod.

Opgaven løses langt bedre med en metode:

for (int x = 0; x < 3; x++)
{
    Tæl();
}
void Tæl()  
{
    for (int i = 1; i < 6; i++)
        Console.WriteLine(i);
}

Nu tælles der til tre og hver gang kaldes en metode, som udskriver værdierne fra 1-6.

Koden kan gøres endnu mere genbrugelig ved at tilføje et argument til Tæl-metoden, så det antal, der skal tælles til, er angivet i kaldet:

Console.WriteLine();
for (int x = 0; x < 3; x++)
{
    Tæl(5);
}

void Tæl(int tælTil)  
{
    for (int i = 1; i < tælTil+1; i++)
        Console.WriteLine(i);
}

Sluttelig kunne hele opgaven løses i en enkelt metode:

Tæl(5, 3);

void Tæl(int tælTil, int gentag)
{
    for (int x = 1; x < gentag + 1; x++)
        for (int i = 1; i < tælTil + 1; i++)
            Console.WriteLine(i);
}

Nu kan Tæl-metoden genbruges overalt, hvor der skal tælles et tal, og eventuelt gentages et antal gange.

For at få et par begreber på plads, så betyder

  • et kald til en metode, at metoden afvikles
  • void-kodeordet foran metoden, at metoden ikke returnerer noget (void betyder tom – se senere)
  • navnet på metoden er Tæl
  • de to argumenter tælTil og gentag er begge heltal, og skal angives i kaldet til metoden.

Programpointeren og metoder

Noget af det, en begynder i programmering (ikke bare C#) kan have svært ved, er at se, hvilke instruktioner som afvikles ved kald til metode og hvornår. Det kaldes også at følge programpointeren, der er den pegepind som kompiler og runtime benytter til at afvikle instruktioner i den rette rækkefølge.

Uden metoder er det nemt nok – du er sikkert ikke tvivl om rækkefølgen, når følgende instruktioner afvikles:

int i = DateTime.Now.Second; // find aktuelt sekund
if (i % 2 == 0) // Hvis det er et lige sekund
{
    Console.WriteLine("Lige");
}
else
{
    Console.WriteLine("Ulige");
}

Koden kan naturligvis kompliceres med en masse løkker og yderligere betingelsesstrukturer, men grundlæggende er det nemt at følge programpointeren. Og du kan jo altid benytte debuggeren og steppe dig igennem koden, hvis du er i tvivl.

Anderledes er det ved metodekald, fordi kompiler og runtime automatisk sørger for at vende retur til instruktionen lige efter et metodekald. Se følgende kode (prøv den gerne selv):

void MinMetode() {
    Console.WriteLine("I metode");
}

Console.WriteLine("Start");
MinMetode();
Console.WriteLine("Slut");

Når du afvikler koden, vil du se:

Start
I metode
Slut

Det er et tydeligt bevis på, at runtime husker, hvilken instruktion der blev afviklet inden metodekald og automatisk vender retur til den efterfølgende instruktion.

Lidt mere kompleks er følgende kode:

void MinMetode1()
{
    Console.WriteLine("I metode 1");
    MinMetode2();
}
void MinMetode2()
{
    Console.WriteLine("I metode 2");
    MinMetode3();
}
void MinMetode3()
{
    Console.WriteLine("I metode 3");
}

Console.WriteLine("Start");
MinMetode1();
Console.WriteLine("Slut");

Bemærk, at MinMetode1 kalder MinMetode2 som kalder MinMetode3, og når runtime har afviklet denne vendes retur til MinMetode2, som dermed er færdigafviklet, og så vendes retur til MinMetode1, og slutteligt vendes der retur til instruktionen lige efter kaldet til MinMetode1. Resultatet bliver dermed:

Start
I metode 1
I metode 2
I metode 3
Slut

Så ved metoder, der kalder andre metoder, husker runtime altså, hvor programpointeren kom fra, og skal nok selv vende retur.

Hvis du er helt ny i programmering, er det en god ide at kopiere ovennævnte kode, og så bruge debuggeren til at steppe dig igennem. Så kan du tydeligt se rækkefølgen. Du skal blot huske at bruge F11 (Step Into), når du stepper igennem koden.

Info

Man kunne måske fristes til at kalde MinMetode2 i slutningen af MinMetode3 for at komme retur, men det vil give en runtime fejl, fordi du ender i et uendeligt loop. Det kaldes også et uendeligt rekursivt loop, og ender med at applikationen opbruger al tildelt hukommelse.

Definition af metoder

En metode har følgende definition:

[synlighed] [[static]] [returtype] Navn ([argumenter])
{
    // kode
    // return ...
}

Synlighed fortæller, hvor metoden kan tilgås og kan i sin grundlæggende form have værdierne public, private samt protected. Indtil videre kan du blot erklære dine metoder offentlige (public).

En metode kan være enten en statisk (static) eller en instans-metode, og det fortæller runtime, om metoden er placeret på et objekt, som først skal oprettes, inden metoden kan benyttes, eller om metoden kan tilgås direkte på selve typen. Hvis du vil lege med forskellige typer af metoder i en standard konsolapplikation, er det nemmeste at definere statiske metoder i Program-klassen, og så kalde dem fra Main-metoden (som i sig selv er statisk). Vi kommer senere mere ind på statiske og instans medlemmer.

Returtypen definerer, hvilken type metoden returnerer. Hvis den ikke returnerer noget, angives typen som void (tom), men ellers kan enhver type angives. Du kan således skabe en metode, der returnerer en int, double, bool, DateTime, File og meget andet.

Navnet på metoden er selvvalgt og skal blot overholde få regler omkring blandt andet brug af specialtegn. De fleste vil navngive metoder med et stort bogstav til at starte med, og herefter benytte camel-casing, som du typisk også ser variabler benytte. Således kan eksempler på metodenavne være Beregn, BeregnAntalDage, KørProgram og så videre. Men der er frit valg ved navngivning.

Du kan angive så mange argumenter til en metode, du ønsker. De skal angives med komma imellem, og alle argumenter skal være omkranset af en parentes. Hvis der ikke er nogle argumenter, skal der angives en tom parentes. De enkelte argumenter skal angives med en type og et navn ligesom ved erklæring af variabler og er typisk navngivet på samme måde. Der er dog helt frit valg.

Selve metodekroppen er omkranset af tuborgklammer, og hvis metoden returnerer andet end void, så skal der angives en (eller flere) return-instruktioner. Return-kodeordet fortæller runtime, hvad metoden konkret returnerer, og hvis kodeordet mangler, kan koden ikke kompileres. Det gælder ikke ved en void-metoden, som blot returnerer efter den sidste instruktion. Man må dog gerne benytte et eller flere return-kodeord i en void-metode for at afslutte før tid.

Placering af metoder

Som tidligere nævnt er alt i C# baseret på de fem typer – klasser, strukturer, interfaces, enumerations og delegates. En metode skal placeres enten i en struktur eller i en klasse, og en af disse typer repræsenterer typisk et element i en applikation. Microsoft bruger eksempelvis strukturer og klasser til at repræsentere variabeltyper som int (struktur) og string (klasse), og disse typer har forskellige metoder. På samme måde kan du oprette klasser og strukturer, og her kan du placere metoder.

Som tidligere nævnt kan der skabes konsol applikationer på to måder – med og uden Program-klassen (uden ”top level statements”). I en helt simpel konsol applikation uden en Program-klasse kan metoder blot placeres i samme fil:

// Definitation af MinMetode
void MinMetode() { }

// Kald til MinMetode
MinMetode();

I en konsol-applikation med en Program-klasse, og den kan du godt udvide klassen med metoder:

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
        MinMetode();
        }

        static void MinMetode() { }
    }
}

Hvis du udvider Program-klassen, er det nemmest at skabe statiske metoder (med static kodeordet) – primært fordi Main i sig selv er statisk og dermed nemt kan tilgå andre statiske metoder.

Metoder der ikke returnerer en værdi

Her er en samling af metoder, du kan bruge som skabelon, indtil du har syntaksen på plads. Først void-metoder – altså metoder som ikke returnerer noget (void betyder tom):

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
                       // eksempler på kald til alle metode
            MinMetode1();
            MinMetode2(4);
            MinMetode3(4, "z");
            MinMetode4(4, "z");
            MinMetode5(4, "z");
        }

        // void-metode uden argumenter
        static void MinMetode1()
        {
            Console.WriteLine("I MinMetode1");
        }

        // void-metode med et enkelt argument
        static void MinMetode2(int a)
        {
            Console.WriteLine($"I MinMetode2 med a={a}");
        }

        // void-metode med to argumenter
        static void MinMetode3(int a, string b)
        {
            Console.WriteLine($"I MinMetode3 med a={a} og b={b}");
        }

        // void-metode med argumenter som kun afvikler kode hvis a>= 5
        static void MinMetode4(int a, string b)
        {
            if (a >= 5)
            {
                Console.WriteLine($"I MinMetode4 med a={a} og b={b}");
            }
        }

        // void-metode med argumenter som kun afvikler kode hvis a>= 5 
        // ved hjælp af return
        static void MinMetode5(int a, string b)
        {
            if (a < 5)
            {
                return;
            }
            Console.WriteLine($"I MinMetode5 med a={a} og b={b}");
        }
    }
}

Bemærk de forskellige måder en void-metode kan oprettes:

  • uden argumenter
  • med argumenter
  • brug af return for at afbryde før tid.
  • Disse eksempler kan du benytte som skabelon til alle typer af void-metoder.

Info

Du kan altså opfatte en void-metode som en mulighed for at genbruge en samling instruktioner – med eller uden argumenter. Der kan ikke returneres noget fra en void-metode.

Metoder der returnerer en værdi

Hvis du ønsker at en metode skal returnere noget, skal du blot erstatte void-kodeordet med den givne type. Her er eksempler på metoder, der returnerer simple datatyper:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // kald til metode hvor returværdi ikke benyttes
            Metode1();
            // kald til metode hvor returværdi gemmes
            bool r1 = Metode1();
            // kald til metode hvor resultat blot udskrives (true)
            Console.WriteLine(Metode1());

            // kald til metode med argument
            bool r2 = Metode2(1);
            // kald til metode med argumenter
            int r3 = Metode3(5, 5);
            // 10
            Console.WriteLine(r3);

            // kald til metode med et enkelt argument
            string r4 = Metode4("mathias");
            // Mathias
            Console.WriteLine(r4);
            // kald til metode hvor resultat blot udskrives (Mikkel)
            Console.WriteLine(Metode4("mikkel"));

            // Kald til metode med flere returns ("")
            Console.WriteLine(Metode5(null));
            // Kald til metode med flere returns ("")
            Console.WriteLine(Metode5(""));
            // Kald til metode med flere returns (Michell)
            Console.WriteLine(Metode5("mIcheLl"));  

        }

        // Metode der returnerer en bool uden argumenter
        static bool Metode1()
        {
            return true;
        }

        // Metode der returnerer en bool med argument
        static bool Metode2(int a)
        {
            bool res = a > 10;
            return res;     // eller blot return a > 10;
        }

        // Metode der returnerer en int to argumenter
        static int Metode3(int a, int b)
        {
            return a + b;            
        }

        // Metode der returnerer en string med et argument
        static string Metode4(string navn)
        {
            string lille = navn.ToLower();
            string førsteBogstav = lille.Substring(0, 1).ToUpper();
            string resten = lille.Substring(1);
            return førsteBogstav + resten;
        }

        // Metode der returnerer en string med et argument
        // med flere return
        static string Metode5(string navn)
        {
            if (navn == null || navn == "")
                return "";
            string lille = navn.ToLower();
            string førsteBogstav = lille.Substring(0, 1).ToUpper();
            string resten = lille.Substring(1);
            return førsteBogstav + resten;
        }

    }
}

Bemærk de forskellige typer, som metoderne returnerer, samt brugen af return i den sidste metode. Du må gerne have flere returns, men en metode med en returtype skal returnere en værdi – ellers vil du få en not all code paths returns a value fejl.

En metode kan returnere alle typer – herunder også dine egne typer (klasser eller strukturer). Det ser vi på senere. Argumenter og variabler

Et klassisk begynderspørgsmål tager udgangspunkt i kode som følger:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {

            Metode1(1, 1);
            int a = 1, b = 1;
            Metode1(a, b);
                               // hvad er værdien af a og b her
            int x = 1, y = 1;
            Metode1(x, y);
            // hvad er værdien af x og y her

        }

        public static void Metode1(int a, int b)
        {
            Console.WriteLine($"I Metode1 med a={a} og b={b}");
            a++;            
            Console.WriteLine($"a har nu værdien {a}");
        }
    }
}

Metoden Metode1 tager to argumenter (a og b) og kaldes tre gange – først med konstanter, så med variablerne kaldet a og b, og slutteligt med variablerne x og y.

Spørgsmålet er, om variablerne a, b, x og y ændrer værdi i Main efter at metoden er kaldt? Svaret er nej – de ændrer ikke værdi.

I Main vil både a, b, x og y have værdien 1 både før og efter kaldet til Metode1. Det gælder altså også variablerne a og b, som jo har samme navn som argumenter a og b i metoden.

En standard C# metode har sit eget lille virkefelt, og det der sker i metoden, har ikke påvirkning i andre virkefelter i programmet. Når en metode kaldes, vil værdier (fra konstanter eller variabler) kopieres ind i metoden som argumenter, og du kan se på argumenterne, som var det variabler i metoden. Hvis du ændrer værdierne af argumenterne i metoden, har det ingen konsekvens for variabler i det virkefelt, som kalder metoden.

Info

I den viste kode er argumenterne simple værdibaserede typer, så her kopieres værdierne ind som argumenter. I mere komplekse situationer kan argumenterne være referencebaserede, og så kan en ændring have konsekvens i det kaldende virkefelt. Det ændrer dog ikke på, at værdien kopieres ind i argumenterne. Når der er tale om referencebaserede variabler, er værdien blot en reference. Mere om referencebaserede variabler senere.

Argumenter som ikke behøves angivet

I nogle situationer giver det mening at argumenter har default værdier, og dermed ikke behøves angivet ved kald. Det klares nemt i C# ved at bruge lig med operatoren efter et argument. Eneste regel er, at argumenter med default værdier skal stå til sidst – ellers kan kompileren ikke regne ud, hvilke værdier der skal tildeles hvilke argumenter.

Her er et eksempel med et enkelt argument:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Kald til metoden med momsPct
            double res1 = BeregnPrisMedMoms(100, 0.25);
            // Kald til metoden uden momsPct 
            // (dermed benyttes 0.25 som værdi)
            double res2 = BeregnPrisMedMoms(100);

            Console.WriteLine(res1);    // 125
            Console.WriteLine(res2);    // 125
        }

        static double BeregnPrisMedMoms(double pris,
           double momsPct = 0.25) {
            return pris * (1 + momsPct);
        }
    }
}

Bemærk, at argumentet momsPct sættes til 0.25 i definitionen af metoden, og det betyder at metoden kan kaldes på to måder:

BeregnPrisMedMoms(100, 0.25);
BeregnPrisMedMoms(100);

Du må benytte så mange defaultværdier til argumenter, du har lyst til, men argumenterne skal placeres til sidst. Navngivne argumenter

Nogle metoder kan have mange argumenter, og det kan gøre det svært at skrive og læse koden, når metoden kaldes. Derfor kan du vælge at angive navne på argumenter ved kald til metoden. Se følgende eksempel:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // standard kald af metoden 
            // værdier skal placeres i korrekt rækkefølge
            Metode1("Mikkel", 16, true, "DK");

            // kald af metoden med navngivne argumenter
            Metode1(navn: "Mikkel", alder: 16, 
                erSmart: true, land: "DK");

            // kald af metoden med navngivne argumenter
            // og nu er rækkefølgen ikke længere vigtig
            Metode1(land: "DK", erSmart: true, 
                alder: 16, navn: "Mikkel");

            // kald af metoden med både unavngivne 
            // og navngivne argumenter            
            Metode1("Mikkel", 16, land: "DK", erSmart: true);
        }

        static void Metode1(string navn, int alder, 
           bool erSmart, string land)
        {
        }
    }
}

Bemærk, at metoden kan kaldes så det tydeligt fremgår, hvilke værdier som skal kopieres til hvilke argumenter, og at unavngivne og navngivne argumenter kan kombineres (med her har rækkefølgen betydning igen).

Info

Der er lidt blandede meninger om, hvorvidt man bør benytte navngivne argumenter – flere mener, det er noget pjat og kan bedre lide at angive argumenter i den rigtige rækkefølge. Det er helt op til dig – hvis du mener, det er nemmere at skrive og læse navngivne argumenter, gør du det. Det ”koster” ikke noget ved afvikling af applikationen.

Metoder med samme navn (overload)

For at gøre kald til relaterede metoder så effektive som muligt, kan du vælge at have flere metoder med samme navn. Det kræver dog, at kompileren kan adskille metoderne fra hinanden ved hjælp af metodens signatur (returværdi og argumenter).

Se følgende eksempel, hvor der findes tre metoder til at beregne moms ud fra forskellige variabeltyper:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            double a = 100;
            decimal b = 100;
            int c = 100;

            Console.WriteLine(BeregnMomsDouble(a, 0.25));       // 125
            Console.WriteLine(BeregnMomsDecimal(b, 0.25));      // 125
            Console.WriteLine(BeregnMomsDouble(c, 0.25));       // 125
        }

        static decimal BeregnMomsDouble(double pris, double momspct) {
            return Convert.ToDecimal(pris * (1 + momspct));
        }

        static decimal BeregnMomsDecimal(decimal pris, double momspct)
        {
            return pris * (1 + Convert.ToDecimal(momspct));
        }

        static decimal BeregnMomsInt32(int pris, double momspct)
        {
            return pris * (1 + Convert.ToDecimal(momspct));
        }
    }
}

Der er tale om tre metoder, som beregner en samlet pris, men gør det på basis af forskellige variabeltyper – derfor er de navngivet:

  • BeregnMomsDouble
  • BeregnMomsDecimal
  • BeregnMomsInt32

Fordi metoderne i virkeligheden har forskellig signatur, kan de godt hedde det samme, og det gør koden mere logisk og læsbar:

using System;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            double a = 100;
            decimal b = 100;
            int c = 100;

            Console.WriteLine(BeregnMoms(a, 0.25));          // 125
            Console.WriteLine(BeregnMoms(b, 0.25));          // 125
            Console.WriteLine(BeregnMoms((double)c, 0.25));  // 125
        }

        static decimal BeregnMoms(double pris, double momspct) {
            return Convert.ToDecimal(pris * (1 + momspct));
        }

        static decimal BeregnMoms(decimal pris, double momspct)
        {
            return pris * (1 + Convert.ToDecimal(momspct));
        }

        static decimal BeregnMoms(int pris, double momspct)
        {
            return pris * (1 + Convert.ToDecimal(momspct));
        }
    }
}

Bemærk, at alle tre metoder nu hedder BeregnMoms, og når du angiver kaldet i eksempelvis Visual Studio, er det meget tydeligt:

Kald af metoder med samme navn.

Info

Metoder med samme navn hedder også et overload, og der findes et hav af overloadede metoder i frameworket. Se eksempelvis hvor mange gange Console.WriteLine-metoden er overloaded.

Brug af navngivne argumenter

I C# kan du bruge navngivne argumenter i dine metodekald for at forbedre læsbarheden af din kode, især når du kalder metoder med mange argumenter eller med argumenter, hvis formål ikke er umiddelbart klart ud fra konteksten. Navngivne argumenter giver dig mulighed for at specificere værdierne for parametrene direkte ved navn, uafhængigt af deres rækkefølge i metode definitionen. Det giver en del fordele:

  1. Forbedret Læsbarhed: Gør det nemmere at forstå, hvad hver parameter repræsenterer, især i metoder med mange parametre eller boolske flag.
  2. Fleksibilitet i Argumentrækkefølge: Tillader dig at specificere argumenter i en hvilken som helst rækkefølge, hvilket kan være nyttigt, når du kun husker parameterens navn.
  3. Undgå Fejl: Reducerer risikoen for fejl ved at bytte om på parametre af samme type.

Forestil dig en metode, der konfigurerer en brugerprofil:

public void KonfigurerBruger(string navn, int alder, bool erAktiv)
{
    // Metode logik...
}

Uden navngivne argumenter kan et kald til denne metode se sådan ud:

KonfigurerBruger("Jens", 30, true);

Selvom dette er korrekt, er det ikke umiddelbart klart, hvad hver parameter repræsenterer, især for personer, der ikke er bekendt med metoden. Ved at anvende navngivne argumenter bliver kaldet mere læsbart:

KonfigurerBruger(navn: "Jens", alder: 30, erAktiv: true);

I dette eksempel er det meget klarere, hvad hver parameter betyder.

Det er også muligt at blande positionelle og navngivne argumenter i samme metodekald, men når du først har startet med navngivne argumenter, skal alle efterfølgende argumenter også være navngivne:

KonfigurerBruger("Jens", alder: 30, erAktiv: true);

Opgaver

Stack frames

Stack frames er en vigtig del af, hvordan C#-programmer udfører metodekald og håndterer argumenter. Når en metode kaldes, oprettes en ny stack frame på call stacken. En stack frame indeholder oplysninger om metodekaldet, såsom parametre, lokale variabler og returadressen, hvilket er den adresse, hvor udførelsen fortsætter, når metoden er færdig.

Når det gælder argumenter i form af structs, er det vigtigt at bemærke, at structs er value typer. Det betyder, at når en struct bruges som argument i et metodekald, laves en kopi af structen, som derefter placeres på stacken. Dette adskiller sig fra reference typer, hvor en reference til objektet (i stedet for selve objektet) placeres på stacken.

Her er et eksempel, der viser, hvordan structs håndteres som argumenter i metodekald:

struct Point
{
    public int X;
    public int Y;
}

class Program
{
    static void Main()
    {
        Point p1 = new Point { X = 3, Y = 4 };
        UpdatePoint(p1);
        Console.WriteLine($"p1: ({p1.X}, {p1.Y})"); // Output: p1: (3, 4)
    }

    static void UpdatePoint(Point p)
    {
        p.X += 1;
        p.Y += 1;
    }
}

I dette eksempel findes en struct Point, og en instans p1 oprettes. Når UpdatePoint-metoden kaldes med p1 som argument, laves en kopi af p1, og den originale p1 forbliver uændret. Dette skyldes, at structs er value typer og kopieres, når de sendes som argumenter til metoder.

Her er en video der beskriver stack frames yderligere:

Grundlæggende forståelse for stack frames

Metoder i metoder

I nyere version af C# er det muligt at placere metoder i metoder.

void Main(string[] args)
{

    int res = LægSammen(1, 1);

    int LægSammen(int a, int b)
    {
        return a + b;
    }

}
Lidt mere avanceret (forudsætter viden om delegates og lambda)
// brug af anonyme metoder
// Lambda - se senere

Func<int, int, int> LægSammen = (a, b) => a + b;
int res = LægSammen(1, 1);
Console.WriteLine($"Resultat: {res}");


/*
---------- Output: ----------

Resultat: 2

*/

Introduktion til Expression Body methods

Fra og med C# 6 har det været muligt at benytte såkaldte expression body members - herunder til definering af metoder der kun har en instruktion (med en implicit return). Den benytter lambda syntaksen (som gennemgås senere), men giver mulighed for at skrive metoder meget hurtigt. Her er et par eksempler på brug af syntaksen:

private static double Gange(double a, double b) => a * b;

er det samme som

private static double Gange(double a, double b)
{
    return  a * b;
}

og

private static void Skriv(string t) => Console.WriteLine(t);

er det samme som

private static void Skriv(string t)
{
     Console.WriteLine(t);
}

Som sagt - mere om Lambda i et senere modul.

Attributter

For at give yderligere information til eksempelvis kompiler eller frameworks som benytter en metode kan den beriges med [attributter]. Det vil tilføjes ekstra metadata som kan aflæses hvis det ønskes. Eksempelvis kan en metode markeres med en ObsolteAttribute for at indikere, at metoden ikke bør benyttes:

[Obsolete()]
public void Test(){}

// Prøv evt [Obsolete(true)]

Nu vil eksempelvis Visual Studio markere metoden som obsolete (evt som fejl).

Argumenter til metoder kan også tilføjes ekstra information og give yderligere funktionalitet. Her er et mere avanceret eksempel som giver log information til en metode.

void CallerInfoTest([CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
{
    Console.WriteLine(callerMemberName);
    Console.WriteLine(callerFilePath);
    Console.WriteLine(callerLineNumber);
}

Her tilføjer runtime info om den kaldende metode (men pas på performance!).

Der må gerne tilføjes mange attributter og man kan skabe dem selv, men lige nu skal du bare vide at det er en måde at tilføje ekstra information til metoder.