NAPREDNI C#(SHARP) PRIMER

U ovom članku je pokazano, kako se kreira složenog primera u C#, konzolne aplikacije, koja prikazuje tehnike objektno-orijentisanog programiranja, klase, objekte, kolekcije, algoritme sortiranja, interfejse, obradu izuzetaka. Takođe je pokazana i upotrba LINQ upita, kao i Lambda izraza. Posmatrajmo sledeći primer:

Primer: Upravljanje putnicima u vozu


Zadatak je da se kreira program koji upravlja putnicima u vozu. Putnik treba da ima informacije kao što su ime, prezime, i način kupovine karte. Potrebno je da se rezervacija automatski potvrdi ako je karta kupljena online ili na šalteru.
Program treba da omogući unos putnika u voz, ali voz može primiti najviše sedam putnika. Takođe, treba omogućiti prikaz svih putnika u vozu, kao i prikaz putnika zajedno sa cenom karte. Cena karte može biti umanjena ako je karta kupljena online, uz određeni popust(npr. 10%).
Dodatno, potrebno je implementirati funkcionalnost za sortiranje putnika po imenu ili prezimenu i prikaz rezultata.
Program treba da omogući i rukovanje greškama koje mogu nastati, na primer, prilikom pokušaja dodavanja više putnika od dozvoljenog  u voz(u ovom primeru je 7).
Napomena: Možete koristiti generičke kolekcije za čuvanje podataka o putnicima.

Sličan primer (zadatak br. 8: Upravljanje porudžbinama u restoranu)možete naći na web strani: Dodatni primeri za vezbu - Klase i objekti

Kreiranje novog projekta i instaliranje potrebnih dodatnih paketa

Otvorimo Visual Studio, a zatim kreirajmo Konzolnu aplikaciju pod nazivom BrziVozApp!C# Složena aplikacija: Kreiranje novog projekta BrziVozApp
Slika 1: C# Složena aplikacija: Kreiranje novog projekta BrziVozApp

Opis primera:

Prvo ćemo kreirati klasu Putnik koja opisuje nekog putnika. Od atributa treba staviti ono što je bitno za ovaj zadatak, a to su opšte informacije o putniku, ime i prezime, zatim način kupovine(online ili šalter) i rezervacija, odnosno da li je putnik rezervisao kartu ili nije.
Da bi pokazali upotrebu interfejsa i njihovo povezivanje sa određenom klasom, kreiraćemo interfejs IVoz, gde ćemo definisane potrebne virtualne metode, koje će se implementirati u klasi, ili klasama koje budemo povezali sa ovim interfejsom. Definisaćemo u interfejsu 3 virtualne metode:
  1. Ulaz() koja prihvata objekat klase Putnik za parametar i koja mora proveriti, da broj putnika ne prelazi ograničenje(7).
  2. CenaKarte() koja za svakog putnika ispisuje cenu karte sa popustom ili bez. Popust se dobija ako se kupi karta onlajn i u ovom primeru iznosi 10%. Kupovinom na šalteru ne dobija se popust, a osnovna cena karte je 1500din.
  3. SpisakPutnika()-ispisuje putnike koji se nalaze u vozu.
Kreiraćemo zatim klasu Voz koja sadrži u sebi niz ili listu putnika, cenu vozne karte , metode za sortiranje, jednu za sortiranje po imenu, a drugu za sortiranje po prezimenu. One putnike koji imaju rezervaciju i koji su kartu kupili "online" sortiraćemo po opadajućem poretku. Takođe ćemo implementirati "indekser" za putnike, da bi pristupali listi putnika kao da je niz.
U glavnoj klasi, kreiraćemo instancu voza, kreirati kolekciju putnika, sortirati je obrnutim redosledom kojim su putnici ulazili u voz.
Ovde treba još uraditi sledeće. Ucitati broj putnika. Ispisati putnike, ali voditi računa da,  ako je broj preko ograničenja aplikacija izbaci izuzetak, sa porukom da je broj putnika prekoračen i u tom slučaju ispiše samo prvih 7 putnika.
Sortirati putnike, najpre po imenu, a zatim i po prezimenu i sortirati. Ovde se mogu koristiti, Linq upiti, lambda izrazi ili koristiti interfejs ICompare.
 Demonstrirati bacanje i hvatanje izuzetaka.

Kreiranje klase Putnik

U okviru postojećeg projekta dodajmo novu klasu Putnik u fajlu Putnik.cs i implementirajmo je.
Ako niste sigurni kako se dodaje nova klasa, pogledajte to u članku: Kreiranje jednostavne aplikacije u C#Dodajmo sledeće atribute: ime, prezime, nacinKupovine, tipa string i rezervacija tipa bool. Dodaćemo i konstruktor sa parametrima u kojem treba uključiti sve parametre sem rezervacije, jel će ona biti programski određena. 
Klasa treba da izgleda kao na slici broj 2.
Slika 2: C# Složena aplikacija: Deo klase Putnik-BrziVozApp
Slika 2: C# Složena aplikacija: Deo klase Putnik-BrziVozApp
Unutar konstruktora je izvršena validacija za rezevaciju, tj. vrednost je true ako je način kupovine "online" ili "šalter".
Dalje treba dodati osobine(Property engl.) za sva polja klase i metodu toString() koja je zadužena za ispis podataka: Kompletna klasa je prikazana na slici broj 3:C# Složena aplikacija: Kompletna klasa Putnik-BrziVozApp
Slika 3: C# Složena aplikacija: Kompletna klasa Putnik-BrziVozApp
Ovde treba uočiti da je validacija za rezervaciju izvršena i u seter metodi property-ja za nacin kupovine. Takođe, metoda ToString() je prepravljena metoda i ima anotaciju override. 
Analiza ToString() metode. Ona vraća red teksta sa podacima.
Taj red koristi C# string interpolaciju za formatiranje stringova. Evo šta se dešava u svakom delu:
​

Implementacija interfejsa IVoz

Kreirajmo nov interfejs pod nazivom IVoz, kao što je pokazano na sledećoj slici. Desnim klikom u Solution Explorer-u sa desne strane, kliknimo na naziv projekta, pa na Add-> NewItem i prikazaće se dijalog (Add New Item), treba odabrati Interface iz liste i kliknuti na Add dugme, vidi sliku 3:C# Složena aplikacija: Dodavanje interfejsa IVoz-BrziVozApp
Slika 4: C# Složena aplikacija: Dodavanje interfejsa IVoz-BrziVozApp
Kreirajmo sada 3 virtuelne metode, kao što je prikazano na slici 5:C# Složena aplikacija: Interfejs IVoz-BrziVozApp
Slika 5: C# Složena aplikacija: Interfejs IVoz-BrziVozApp
Svi dosadašnje kreirani fajlovi vidljivi su u Solution Explorer-u, kao što se može videti na slici 6:C# Složena aplikacija: Solution Explorer-BrziVozApp
Slika 6: C# Složena aplikacija: Solution Explorer-BrziVozApp
Kreirajmo klasu Voz, ali pre toga ćemo kreirati klasu za obradu izuzetaka, pod nazivom IzuzetakVoz, s obzirom da ćemo ga koristiti u klasi Voz za proveru ulaza putnika. Kreirajmo sada klasu za izuzetke.

Kreiranje klase za obradu izuzetaka(IzuzetakVoz)

IzuzetakVoz (vidi sliku 7.) je klasa koja nasleđuje klasu Exception. Ovo znači da je IzuzetakVoz prilagođeni (custom) izuzetak, koji se može koristiti za signaliziranje specifičnih problema u aplikaciji. C# Složena aplikacija: Klasa IzuzetakVoz-BrziVozApp
Slika 7: C# Složena aplikacija: Klasa IzuzetakVoz-BrziVozApp
​Hajde da razložimo ovu klasu:
internal class IzuzetakVoz : Exception
Klasa IzuzetakVoz nasleđuje klasu Exception, koja je osnovna klasa za sve izuzetke u C#. Kada nasledte Exception, dobijate mogućnost da definišete vlastite izuzetke koji se mogu hvatati u try-catch blokovima.
​
Konstruktor sa parametrom string
​

public IzuzetakVoz(string poruka) : base(poruka)
{
Console.WriteLine(poruka);
}
U ovom primeru, kada se dogodi situacija koja zahteva specifičan izuzetak, što će se desiti u klasi Voz, koju tek treba da implementiramo, može se throw-ovati novi IzuzetakVoz sa odgovarajućom porukom. U catch bloku može se uhvatiti taj izuzetak i obraditi na specifičan način, kao što je prikazivanje poruke korisniku ili zapisivanje u log fajl.

Implementacija klase Voz i povezivanje sa interfejsom IVoz

Kreirajmo nov fajl Voz.cs, koji će sadržati ovu klasu i napravimo da nasleđuje interfejs IVoz. Program će prijavljivati grešku, sve dok virtuelne metode iz interfejsa ne implementiramo, odnosno dodamo im telo, što se može videti na slici 8:C# Složena aplikacija: Klasa Voz, implementacija interfejsa IVoz-BrziVozApp
Slika 8: C# Složena aplikacija: Klasa Voz, implementacija interfejsa IVoz-BrziVozApp
Kliknimo na lampicu i na ponuđenu opciju da implementiramo metode iz povezanog interfejsa. Metode se dodaju, dobijaju telo, ali sadržaj tela mora biti promenjen jer trenutno stoji sledeći red koji izbacuje izuzetak ako sada pokušamo da pokrenemo aplikaciju:
throw new NotImplementedException();

Kreiranje liste putnika

Listu deklarišemo unutar klase, a dovršavamo kreiranje u konstruktoru:
internal class Voz : IVoz
{
List putnici;//Deklaracija liste putnika
private double cenaPutnika;

/*Konstruktor*/
public Voz()
{
cenaPutnika = 1500;//Podrazumevana cena
putnici = new List();//Kreiranje prazne liste do kraja
}
}

Metoda Ulaz

Metoda Ulaz(Putnik p) treba da doda, poslatog(kao parametar), putnika(objekat klase Putnik), u listu. Prvo se proverava da li je broj putnika u listi premašio ograničenje i ako jeste baca se izuzetak i ovde je prilika da se iskoristi prethodno napravljena klasa za obradu izuzetaka. Metoda je prikazana u nastavku:
public void Ulaz(Putnik putnik)
{
if (putnici.Count >= 7)
{
/*Izbacuje se izuzetak i salje poruka*/
throw new IzuzetakVoz("U vozu sme biti najvise 7 putnika");
}
putnici.Add(putnik);//Dodaje putnika u listu
}

Metoda CenaKarte

Ova metoda svakom od putnika ispisuje cenu karte. Za prolazak kroz listu se koristi foreach naredba, koja u svakom ciklusu uzima sledećeg putnika iz liste i onda za njega proverava da li ima rezervaciju i da li je način plaćanja "online". Ako jeste onda je cena sa popustom od 10%, ako je rezervisano , a način plaćanja na šalteru, onda se prikazuje podrazumevana cena. Metoda je prikazana u nastavku:
public void CenaKarte(double ck)
{
foreach (Putnik p in putnici)
{
if (p.Rezervacija)
{
if (p.NacinKupovine == "online")
{
double cena = 0.9 * ck;//cena sa popustom od 10%
Console.WriteLine($"Putnik: {p.ToString()}, cena karte sa popustom: {cena}din");
}
else if (p.NacinKupovine == "šalter")
{
Console.WriteLine($"Putnik: {p.ToString()}, cena karte: {ck}din");
}
}
}
}
Ova klasa se može videti na sledećoj, slickama br. 9 i 10:C# Složena aplikacija: Klasa Voz, prvi deo-BrziVozApp
Slika 9: C# Složena aplikacija: Klasa Voz, prvi deo-BrziVozApp
Metoda SpisakPutnika se vidi na slici br. 10 i ona služi da ispiše podatke o putnicima. I ovde se koristi forech naredba za prolazak kroz putnike i za svakog putnika se za ispis podataka koristi ToString() metoda definisana u klasi Putnik.Picture
Slika 10: C# Složena aplikacija: Klasa Voz, drugi deo-BrziVozApp

Dodavanje Indeksera unutar metode

Indekser u C# omogućava da objekti klase mogu biti indeksirani kao nizovi. To znači da se može pristupiti elementima u klasi koristeći sintaksu sličnu indeksiranju nizova, na primer myObject[index].
Implementacija IndekseraIndekser se implementira koristeći ključnu reč this, koja se koristi kao specijalna metoda za indeksiranje. Ovde je primer indeksera u klasi Voz, koja sadrži listu putnika, vidi sliku 11:C# Složena aplikacija: Klasa Voz, treći deo, dodavanje indeksera-BrziVozApp
Slika 11: C# Složena aplikacija: Klasa Voz, treći deo, dodavanje indeksera-BrziVozApp

Metode za sortiranje

Da bi kreirali metodu za sortiranje po imenu koristićemo LINQ upit za filtriranje i lambda izraz. 

LINQ (Language Integrated Query) je moćan alat u .NET frameworku koji omogućava upit i manipulaciju podacima iz različitih izvora koristeći isti jezik upita, direktno unutar C# (ili drugih .NET jezika). LINQ pojednostavljuje rad sa podacima, bilo da su oni u memoriji, u bazi podataka, u XML dokumentima ili negde drugde.

Lambda izrazi su ključni deo modernih jezika programiranja kao što je C# i omogućavaju da se pišu inline, anonimne funkcije. U suštini, lambda izrazi omogućavaju definisanje metoda bez potrebe za kreiranjem zasebnih metoda ili klasa.

Više o tome možete pročitati na strani: LINK UPITI I LAMBDA IZRAZI U C#

Da bi kreirali drugu metodu za sortiranje po prezimenu, kreira'emo drugu metodu i koristiti unutrašnju klasu PrezimeComparer koja nasleđuje interfejs IComparer i implementira u sebi metodu compare iz tog interfejsa. Pogledajmo sada dodat kod u istoj klasi Voz na slici 12.C# Složena aplikacija: Klasa Voz, četvrti deo, metode za sortiranje-BrziVozApp
Slika 12: C# Složena aplikacija: Klasa Voz, četvrti deo, metode za sortiranje-BrziVozApp

Dodatna pojašnjenja

Glavna klasa

U glavnoj klasi kreiraćemo instancu voza, kreirati kolekciju putnika, sortirati je obrnutim redosledom kojim su putnici ulazili u voz.
 Potrebno je ucitati broj putnika. Ispisati putnike, ali voditi računa da,  ako je broj preko ograničenja aplikacija izbaci izuzetak, sa porukom da je broj putnika prekoračen i u tom slučaju ispiše samo prvih 7 putnika.
Sortirati putnike, najpre po imenu, a zatim i po prezimenu i sortirati.  Demonstrirati bacanje i hvatanje izuzetaka.
​Pogledajmo sad kako bi trebalo da izgleda glavna klasa na slikama 13 i 14:Picture
Slika 13: C# Složena aplikacija: Glavna Klasa , prvi deo -BrziVozApp
I drugi deo Main metode:C# Složena aplikacija: Glavna Klasa , drugi deo -BrziVozApp
Slika 14: C# Složena aplikacija: Glavna Klasa , drugi deo -BrziVozApp

Objašnjenje koda u glavnoj klasi

Posle pokretanja za unos od 10 putnika dobićemo:C# Složena aplikacija: BrziVozApp, izvršenje aplikacije
Slika 15: C# Složena aplikacija: BrziVozApp, izvršenje aplikacije