Programski jezici → C# → Klase i objekti u C#: polja, konstruktori i svojstva
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
Uputstvo
Skraćivanje: podeli brojilac i imenilac njihovim NZD-om. Zbir: a/b + c/d = (a·d + c·b) / (b·d) – konstruktor ga skraćuje sam.
Rešenje (C#)
using System;
class Razlomak
{
public int Brojilac { get; }
public int Imenilac { get; }
public Razlomak(int brojilac, int imenilac)
{
int d = Nzd(brojilac, imenilac);
Brojilac = brojilac / d;
Imenilac = imenilac / d;
}
static int Nzd(int a, int b) => b == 0 ? a : Nzd(b, a % b);
public Razlomak Saberi(Razlomak r) =>
new Razlomak(Brojilac * r.Imenilac + r.Brojilac * Imenilac, Imenilac * r.Imenilac);
public override string ToString() => $"{Brojilac}/{Imenilac}";
}
class Program
{
static void Main()
{
int[] x = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
var a = new Razlomak(x[0], x[1]);
var b = new Razlomak(x[2], x[3]);
Console.WriteLine(a.Saberi(b));
}
}
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
Uputstvo
Red i mesto od 1 pretvaraju se u indekse od 0 (red - 1). Prvo proveri granice, pa zauzetost. Slobodno neka bude svojstvo sa private set, koje se smanjuje pri svakoj uspešnoj rezervaciji.
Rešenje (C#)
using System;
class Sala
{
private readonly bool[,] zauzeto;
public int Slobodno { get; private set; }
public Sala(int redova, int mesta)
{
zauzeto = new bool[redova, mesta];
Slobodno = redova * mesta;
}
public string Rezervisi(int red, int mesto)
{
if (red < 1 || red > zauzeto.GetLength(0) || mesto < 1 || mesto > zauzeto.GetLength(1))
return "NE POSTOJI";
if (zauzeto[red - 1, mesto - 1])
return "ZAUZETO";
zauzeto[red - 1, mesto - 1] = true;
Slobodno--;
return "OK";
}
}
class Program
{
static void Main()
{
int[] rm = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
var sala = new Sala(rm[0], rm[1]);
int k = int.Parse(Console.ReadLine());
for (int i = 0; i < k; i++)
{
int[] z = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
Console.WriteLine(sala.Rezervisi(z[0], z[1]));
}
Console.WriteLine($"Slobodno: {sala.Slobodno}");
}
}
Č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)?
Podešavanja kolačića
Koristimo kolačiće da bismo vam pružili najbolje moguće iskustvo. Takođe nam omogućavaju da analiziramo ponašanje korisnika kako bismo stalno poboljšavali veb lokaciju za vas.