Klase i objekti u C#: polja, konstruktori i svojstva

Lekcija 7 od 15 · C#

Klasa je opis (šablon) neke vrste podataka: koje podatke ima i šta ume da radi. Objekat je konkretan primerak klase, napravljen operatorom new. U ovoj lekciji su polja, konstruktori, metode objekata, svojstva (properties) i statički članovi – osnova objektno orijentisanog programiranja u C#-u.

Pre nego što počneš: potrebno predznanje – metode, nizovi.
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.

Klasa, objekat i konstruktor

using System;

class Ucenik
{
    // polja – podaci koje ima svaki objekat
    public string Ime;
    public int Razred;

    // konstruktor – poziva se pri pravljenju objekta, ime mu je isto kao ime klase
    public Ucenik(string ime, int razred)
    {
        Ime = ime;
        Razred = razred;
    }

    // metoda objekta – radi sa podacima baš tog objekta
    public void Predstavi()
    {
        Console.WriteLine($"Ja sam {Ime} i idem u {Razred}. razred.");
    }
}

class Program
{
    static void Main()
    {
        Ucenik u1 = new Ucenik("Ana", 7);
        var u2 = new Ucenik("Marko", 8);
        u1.Predstavi();
        u2.Predstavi();
        u2.Razred = 9;                 // javno polje može se menjati spolja
        Console.WriteLine($"{u2.Ime} je sada u {u2.Razred}. razredu.");
    }
}

Svaki objekat ima svoje vrednosti polja: u1.Ime i u2.Ime su različiti podaci. Reč public znači da je član dostupan i van klase, a private (podrazumevano) da je dostupan samo unutar klase.

Enkapsulacija i svojstva (properties)

Podaci objekta se obično sakrivaju (private), a spolja im se pristupa kontrolisano – preko metoda i svojstava. Svojstvo izgleda kao polje, ali ima delove get (čitanje) i set (upis) u kojima može da se proveri vrednost.

using System;

class Racun
{
    private decimal stanje;                   // privatno polje – spolja nedostupno

    public string Vlasnik { get; }            // svojstvo samo za čitanje (postavlja ga konstruktor)
    public decimal Stanje => stanje;          // izračunato svojstvo samo za čitanje

    public Racun(string vlasnik, decimal pocetno)
    {
        Vlasnik = vlasnik;
        stanje = pocetno;
    }

    public void Uplati(decimal iznos)
    {
        if (iznos <= 0)
        {
            Console.WriteLine("Iznos mora biti pozitivan.");
            return;
        }
        stanje += iznos;
    }

    public bool Isplati(decimal iznos)
    {
        if (iznos > stanje) return false;     // nema dovoljno novca – stanje se ne menja
        stanje -= iznos;
        return true;
    }
}

class Program
{
    static void Main()
    {
        var r = new Racun("Jelena", 1000);
        r.Uplati(500);
        r.Uplati(-20);
        Console.WriteLine(r.Isplati(2000) ? "Isplaceno" : "Nema dovoljno sredstava");
        Console.WriteLine(r.Isplati(300) ? "Isplaceno" : "Nema dovoljno sredstava");
        Console.WriteLine($"{r.Vlasnik}: {r.Stanje} din");
        // r.stanje = 1000000;                // greška: polje je private
    }
}

Stanje računa sada ne može da se promeni „mimo pravila“ – samo kroz Uplati i Isplati. To se zove enkapsulacija.

using System;

class Pravougaonik
{
    private double sirina = 1;

    public double Sirina
    {
        get { return sirina; }
        set { sirina = value > 0 ? value : 1; }   // value = vrednost koja se upisuje
    }

    public double Visina { get; set; } = 1;       // automatsko svojstvo sa početnom vrednošću

    public double Povrsina => Sirina * Visina;     // izračunato svojstvo

    public override string ToString() => $"Pravougaonik {Sirina} x {Visina}, P = {Povrsina}";
}

class Program
{
    static void Main()
    {
        var p = new Pravougaonik { Sirina = 4, Visina = 2.5 };   // inicijalizator objekta
        Console.WriteLine(p);                                     // poziva se ToString()
        p.Sirina = -3;                                            // nevažeća vrednost
        Console.WriteLine(p.Sirina);
        Console.WriteLine(p);
    }
}

Metoda ToString() određuje kako se objekat ispisuje. Reč override znači da menjamo postojeću metodu koju svaka klasa nasleđuje od klase object – više o tome u lekciji o nasleđivanju.

Statički članovi i niz objekata

Statičko polje ili metoda (static) pripada klasi, a ne pojedinačnom objektu – postoji samo jedna vrednost, zajednička za sve objekte. Poziva se preko imena klase: Proizvod.BrojProizvoda, kao Math.Sqrt.

using System;

class Proizvod
{
    public static int BrojProizvoda = 0;      // zajedničko za sve objekte

    public string Naziv { get; }
    public decimal Cena { get; set; }

    public Proizvod(string naziv, decimal cena)
    {
        Naziv = naziv;
        Cena = cena;
        BrojProizvoda++;
    }
}

class Program
{
    static void Main()
    {
        Proizvod[] korpa =
        {
            new Proizvod("Sveska", 120),
            new Proizvod("Olovka", 45.5m),
            new Proizvod("Lenjir", 80)
        };
        decimal ukupno = 0;
        foreach (var p in korpa)
        {
            Console.WriteLine($"{p.Naziv,-8}{p.Cena,8:F2}");
            ukupno += p.Cena;
        }
        Console.WriteLine($"{"Ukupno",-8}{ukupno,8:F2}");
        Console.WriteLine($"Napravljeno proizvoda: {Proizvod.BrojProizvoda}");
    }
}

Promenljiva čuva referencu na objekat

Promenljiva tipa klase ne sadrži sam objekat, nego referencu (adresu) objekta. Dodela t2 = t1 ne pravi nov objekat, a == za klase proverava da li je to isti objekat.

using System;

class Tacka
{
    public int X, Y;
    public Tacka(int x, int y) { X = x; Y = y; }
    public override string ToString() => $"({X}, {Y})";
}

class Program
{
    static void Main()
    {
        var t1 = new Tacka(1, 2);
        var t2 = t1;                  // t2 pokazuje na ISTI objekat
        t2.X = 10;
        Console.WriteLine(t1);        // promena se vidi i preko t1
        var t3 = new Tacka(10, 2);    // nov objekat sa istim vrednostima
        Console.WriteLine(t1 == t2);
        Console.WriteLine(t1 == t3);
    }
}

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: srednje

Razlomak

Napisati klasu Razlomak sa svojstvima Brojilac i Imenilac. Konstruktor odmah skraćuje razlomak, metoda Saberi vraća nov razlomak (zbir), a ToString vraća zapis a/b. Učitati dva razlomka (četiri pozitivna broja u redu: brojilac i imenilac prvog, pa drugog) i ispisati njihov zbir.

Primer 1

Ulaz

1 6 1 3

Izlaz

1/2

Primer 2

Ulaz

3 4 5 6

Izlaz

19/12
ZADATAK 02Težina: srednje

Rezervacije u bioskopu

Napisati klasu Sala koja pamti zauzeta mesta (matrica bool[,]) i ima metodu Rezervisi(red, mesto) koja vraća OK, ZAUZETO ili NE POSTOJI, i svojstvo Slobodno. Ulaz: broj redova i broj mesta u redu, zatim broj rezervacija k i k redova sa parom red mesto (numeracija od 1). Ispisati odgovor za svaku rezervaciju, pa broj slobodnih mesta.

Primer

Ulaz

3 4
5
1 1
2 3
1 1
4 1
3 4

Izlaz

OK
OK
ZAUZETO
NE POSTOJI
OK
Slobodno: 9
Česte greške i saveti
  • Ime konstruktora je isto kao ime klase i nema povratni tip (ni void).
  • Polja drži private, a spolja otvori samo ono što je potrebno – svojstva i metode.
  • Bez new nema objekta: promenljiva tipa klase bez dodele ima vrednost null.
  • Statički član se poziva preko imena klase, a ne preko objekta.

Proveri znanje

Otvori kviz

Klase i objekti u C#

1. Šta pravi operator new?




2. Kada se poziva konstruktor?




3. Čemu služi deo set u svojstvu?




4. Posle var t2 = t1; t2.X = 10; koliko je t1.X (Tacka je klasa)?