Metode u C#: parametri, povratna vrednost, ref, out i rekurzija

Lekcija 6 od 15 · C#

Metoda je imenovani deo programa koji obavlja jedan zadatak. Metode skraćuju program, sprečavaju ponavljanje koda i olakšavaju traženje grešaka. Ovde su statičke metode, koje se pozivaju bez objekta; metode objekata dolaze u lekciji o klasama.

Pre nego što počneš: potrebno predznanje – nizovi i stringovi.
Kako da učiš: svaki primer prekucaj i pokreni, a zadatke na kraju prvo reši sam/a; uputstvo i rešenje otvori tek posle toga. Svi programi su konzolne aplikacije u C#-u (.NET 8 ili noviji, bez „top-level statements“); svaki je proveren i daje navedeni izlaz.

Definisanje i poziv metode

Metoda ima zaglavlje (modifikatori, tip povratne vrednosti, ime, parametri) i telo u vitičastim zagradama. Naredba return vraća rezultat i završava metodu. Imena metoda se pišu velikim početnim slovom.

using System;

class Program
{
    // static: metoda pripada klasi, poziva se bez objekta
    // int: tip povratne vrednosti; (int x): parametar
    static int Kvadrat(int x)
    {
        return x * x;
    }

    // void: metoda ništa ne vraća, samo nešto uradi
    static void Linija(int duzina, char znak)
    {
        Console.WriteLine(new string(znak, duzina));
    }

    static void Main()
    {
        Linija(20, '=');
        for (int i = 1; i <= 4; i++)
            Console.WriteLine($"{i} na kvadrat je {Kvadrat(i)}");
        Linija(20, '-');
        Console.WriteLine($"3^2 + 4^2 = {Kvadrat(3) + Kvadrat(4)}");
    }
}

Pri pozivu se argumenti (stvarne vrednosti) dodeljuju parametrima redom: u pozivu Linija(20, '=') parametar duzina dobija 20, a znak dobija '='.

using System;

class Program
{
    static bool JeProst(int n)
    {
        if (n < 2) return false;
        for (int d = 2; d * d <= n; d++)
            if (n % d == 0) return false;   // return odmah završava metodu
        return true;
    }

    static int ZbirCifara(int n)
    {
        int z = 0;
        while (n > 0)
        {
            z += n % 10;
            n /= 10;
        }
        return z;
    }

    static int Veci(int a, int b) => a > b ? a : b;   // metoda u jednom redu (=>)

    static void Main()
    {
        string[] d = Console.ReadLine().Split(' ');
        int a = int.Parse(d[0]), b = int.Parse(d[1]);
        // prosti brojevi iz intervala čiji je zbir cifara takođe prost
        for (int x = a; x <= b; x++)
            if (JeProst(x) && JeProst(ZbirCifara(x)))
                Console.Write(x + " ");
        Console.WriteLine();
        Console.WriteLine($"Veci od {a} i {b} je {Veci(a, b)}");
    }
}

Metoda koja vraća bool čini uslov čitljivim: if (JeProst(n)) se čita kao rečenica. Kratka metoda se može napisati u jednom redu, posle oznake =>.

Prenos argumenata: vrednost, ref i out

Podrazumevano metoda dobija kopiju vrednosti argumenta, pa promena parametra ne utiče na promenljivu iz poziva. Sa ref metoda radi sa samom promenljivom, a out služi da metoda vrati više rezultata. Reč ref ili out piše se i u pozivu.

using System;

class Program
{
    static void Uvecaj(int x)
    {
        x += 10;                        // menja se samo kopija
    }

    static void UvecajRef(ref int x)
    {
        x += 10;                        // menja se promenljiva iz poziva
    }

    static void MinMax(int[] niz, out int min, out int max)
    {
        min = max = niz[0];             // out parametri moraju dobiti vrednost
        foreach (int v in niz)
        {
            if (v < min) min = v;
            if (v > max) max = v;
        }
    }

    static void Main()
    {
        int a = 5;
        Uvecaj(a);
        Console.WriteLine($"Posle Uvecaj: {a}");
        UvecajRef(ref a);
        Console.WriteLine($"Posle UvecajRef: {a}");

        MinMax(new[] { 7, -3, 12, 0 }, out int mn, out int mx);
        Console.WriteLine($"min = {mn}, max = {mx}");
    }
}

Sada je jasno kako radi int.TryParse(tekst, out int broj): metoda vraća bool, a broj upisuje kroz out parametar. Kod nizova i objekata metoda dobija kopiju reference, pa promene elemenata niza ostaju vidljive i bez ref.

Podrazumevani parametri, params i preklapanje

using System;

class Program
{
    // preklapanje: isto ime, različiti parametri
    static double Povrsina(double a) => a * a;               // kvadrat
    static double Povrsina(double a, double b) => a * b;     // pravougaonik

    // podrazumevana vrednost parametra
    static string Pozdrav(string ime, string jezik = "sr") =>
        jezik == "en" ? $"Hello, {ime}!" : $"Zdravo, {ime}!";

    // params: proizvoljan broj argumenata
    static int Zbir(params int[] brojevi)
    {
        int z = 0;
        foreach (int b in brojevi) z += b;
        return z;
    }

    static void Main()
    {
        Console.WriteLine(Povrsina(4));
        Console.WriteLine(Povrsina(4, 2.5));
        Console.WriteLine(Pozdrav("Ana"));
        Console.WriteLine(Pozdrav("Ana", "en"));
        Console.WriteLine(Pozdrav(jezik: "en", ime: "Marko"));   // imenovani argumenti
        Console.WriteLine(Zbir(1, 2, 3));
        Console.WriteLine(Zbir(10, 20, 30, 40));
        Console.WriteLine(Zbir());
    }
}

Kompajler bira metodu po broju i tipu argumenata. Dve metode ne mogu se razlikovati samo po tipu povratne vrednosti.

Rekurzija: metoda koja poziva samu sebe

Rekurzivna metoda rešava zadatak tako što poziva sebe za manji problem. Mora imati izlazni slučaj koji se rešava bez novog poziva – inače se program ruši greškom StackOverflowException.

using System;

class Program
{
    static long Faktorijel(int n) => n <= 1 ? 1 : n * Faktorijel(n - 1);

    static int Nzd(int a, int b) => b == 0 ? a : Nzd(b, a % b);      // Euklidov algoritam

    static string Binarno(int n) => n < 2 ? n.ToString() : Binarno(n / 2) + (n % 2);

    static void Main()
    {
        Console.WriteLine($"10! = {Faktorijel(10)}");
        Console.WriteLine($"NZD(84, 36) = {Nzd(84, 36)}");
        Console.WriteLine($"37 binarno = {Binarno(37)}");
    }
}

Više o rekurziji i o tome kada je bolja petlja: Rekurzija.

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno tačna na primerima iz teksta.

ZADATAK 01Težina: lako

NZD i NZS

Učitati dva prirodna broja i ispisati njihov najveći zajednički delilac i najmanji zajednički sadržalac. Napisati metodu static long Nzd(long a, long b).

Primer 1

Ulaz

12 18

Izlaz

NZD = 6
NZS = 36

Primer 2

Ulaz

7 5

Izlaz

NZD = 1
NZS = 35
ZADATAK 02Težina: srednje

Digitalni koren

Digitalni koren broja dobija se tako što se sabiraju njegove cifre, pa cifre zbira, sve dok ne ostane jednocifren broj (98765 → 35 → 8). Napisati rekurzivne metode ZbirCifara i DigitalniKoren i ispisati digitalni koren učitanog broja (broj staje u long).

Primer 1

Ulaz

98765

Izlaz

8

Primer 2

Ulaz

493193

Izlaz

2
Česte greške i saveti
  • Metoda se piše unutar klase, ali van metode Main.
  • Metoda koja nije void mora vratiti vrednost u svakom slučaju – inače greška „not all code paths return a value“.
  • Lokalne promenljive jedne metode ne vide se u drugoj; podatke prenosi kroz parametre i povratnu vrednost.
  • Reči ref i out pišu se i u zaglavlju metode i u pozivu.

Proveri znanje

Otvori kviz

Metode u C#

1. Šta znači void u zaglavlju metode?




2. Metoda promeni vrednost parametra tipa int (bez ref). Šta se dešava sa promenljivom iz poziva?




3. Čemu služi out parametar?




4. Šta svaka rekurzivna metoda mora da ima?