Dokumentacija kode NPVaje (podrobno, temna tema)

Uvod in ključni koncepti

Ta del daje pregled splošnih pojmov, ki so uporabljeni v celotni kodi. Oblikovanje aplikacije v C# temelji na naslednjih osnovnih gradnikih:

Kaj je izjema (Exception)?

Izjema predstavlja nenormalno situacijo pri izvajanju kode. Če na primer skušamo odstraniti oceno, ki ne obstaja, ali prodati več steklenic, kot jih imamo na zalogi, se vrže izjema. S tem zagotovimo:

Kaj je konstruktor?

Konstruktor je posebna metoda, ki se izvede takoj ob ustvarjanju novega objekta (z ukazom new Razred()). V konstruktorju nastavimo vse ključne lastnosti, inicializiramo seznamne kolekcije ali pričnemo z privzetimi vrednostmi. Če obstaja več konstruktorjev (preobremenjenih), izberemo ustreznega glede na zagotovljene parametre.

Primer osnovnega konstruktorja v C#:

public Vino()
{
    Ocene = new List<int>();
    // Sistem privzeto inicializira vse druge referenčne tipe na null, vrednostne tipe na 0.
    // Tukaj želimo, da je seznam ocen vedno vsaj prazen seznam, ne null.
}
    

Uporaba metode ToString()

Metoda ToString() je v C# privzeto na voljo v vseh razredih (deduje se iz object). Če jo "prepišemo" (override), lahko vrnemo poljuben opis objektov, razumljiv tudi neprogramerjem. To uporabimo kjerkoli želimo izpisati povzetek stanja objekta, na primer:

public override string ToString()
{
    return $"{Ime} ({LetoTrgatve}) - {SortaGrozdja}, Zaloga: {SteviloSteklenic}, Cena: {CenaNaSteklenico}€";
}
    

Če želimo npr. izpisati vsebino seznama List<Vino>, lahko povemo:

foreach (var v in seznamVin)
{
    Console.WriteLine(v.ToString());
}
    

Ključni gradniki aplikacije

Če želimo razviti robustno aplikacijo, upoštevamo naslednje korake:

  1. Določitev domen (Domain Modeling): definiramo, katere entitete potrebujemo (Vino, Vinograd, Delavec, Trgatev, Certifikat itd.).
  2. Definicija lastnosti (Properties): za vsako entiteto zapišemo, katere podatke shranjujemo (podatkovni tipi, omejitve).
  3. Uvod v enume in vmesnike: kje potrebujemo vnaprej določene vrednosti (enum), kje definiramo ogrodje metod (interface), da lahko kasneje zamenjamo implementacijo (npr. različni načini prodaje, več formatov izvoza).
  4. Implementacija razredov: napišemo konstruktorje za varno inicializacijo, metode za poslovno logiko (prodaja, ocenjevanje, izračuni, sortiranje, filtriranje).
  5. Obvladovanje napak (Exceptions): kam vržemo izjeme (konstruktor, metode), kje jih ujamemo, kje seveda dovolimo prekinitve. Dober vzorec je: metodi, ki lahko naletita na nepričakovano razmerje (npr. prevelika količina pri prodaji), vržemo lastno izjemo (PrevecSteklenicException); višje plasti aplikacije (npr. UI ali glavni zagon) pa ujamemo in ustrezno izpišemo sporočilo uporabniku.
  6. Metoda Main in vmesni sloj (I/O): tu se skliče branje/pisanje iz zunanjih virov (datoteke, baza). Prijavimo se na dogodke (subscribe) za uspešno/neuspešno branje/pisanje ter ustrezno poskrbimo za povratno sporočilo uporabniku.
  7. Testiranje: enotni testi za metode, ki izračunajo ceno, dodajajo/odstranjujejo ocene, prodajo itd. Tako zagotovimo, da se poslovna logika ob spremembi kode ne zlomi.
  8. Dokumentacija: jasno opišemo, kateri konstruktorji obstajajo, kaj naredi vsaka metoda, katere izjeme se lahko vržejo in v kakšnih primerih, da lahko bodoči razvijalci ali uporabniki knjižnice hitro najdejo, kaj iščejo.

V nadaljevanju sledijo konkretni razredi in podrobne razlage z vzorčnimi primeri.

Pojdi nazaj na vrh

Osnovni pojmi v C#

V tem poglavju so razloženi splošni jezikovni koncepti, kot so this, base, dostopnost (public/private), lastnosti (get/set), ključne besede sealed, override, virtual, dedovanje in polimorfizem.

this

Ključna beseda this se uporablja znotraj metode ali konstruktorja, da sklicujemo na trenutno instanco razreda. Omogoča dostop do lastnosti in metod trenutnega objekta. Primer:

public class Primer
{
    private string ime;

    public Primer(string ime)
    {
        // this.ime se sklicuje na polje razreda, ime pa na parameter
        this.ime = ime;
    }

    public void Izpisi()
    {
        Console.WriteLine($"Ime objekta je: {this.ime}");
    }
}
    

base

Ključna beseda base se uporablja znotraj podedovanega razreda, da sklicujemo na člane osnovnega (rodovnega) razreda, kot so konstruktor, lastnosti ali metode, ki smo jih v podrazredu morda prepisali (override). Primer:

public class Oseba
{
    public string Ime { get; set; }

    public Oseba(string ime)
    {
        Ime = ime;
    }

    public virtual void PredstaviSe()
    {
        Console.WriteLine($"Sem oseba z imenom {Ime}.");
    }
}

public class Student : Oseba
{
    public int VpisnaStevilka { get; set; }

    public Student(string ime, int vpisna)
        : base(ime) // kliče konstruktor razreda Oseba(ime)
    {
        VpisnaStevilka = vpisna;
    }

    public override void PredstaviSe()
    {
        // base.PredstaviSe() pokliče izvedbo metode iz razreda Oseba
        base.PredstaviSe();
        Console.WriteLine($"Sem tudi študent, vpisna številka: {VpisnaStevilka}.");
    }
}
    

Dostopni modi (public in private)

V C# določimo vidnost (accessibility) članov razreda z dostopnimi modifikatorji. Najpogostejši sta public in private:

Primer omejitev:

public class BankaRacun
{
    // To polje je zasebno – lahko dostopamo do njega samo znotraj tega razreda
    private double stanje;

    // Uporabnik uporablja to javno lastnost (property), da pridobi stanje
    public double Stanje
    {
        get { return stanje; }
        private set { stanje = value; }
        // set je private – samo znotraj razreda lahko spreminjamo stanje
    }

    public void DodajPolog(double znesek)
    {
        if (znesek > 0)
        {
            Stanje += znesek;
        }
    }
}
    

Lastnosti (get in set)

Namesto javnih polj se v C# uporablja mehanizem lastnosti (properties), ki omogoča nadzor nad brskanjem in nastavljanjem vrednosti. Lastnost vsebuje get in/ali set metode (accessorje), ki se izvedejo, ko poskušamo prebrati ali zapisati vrednost.

private int letoRojstva;
public int Starost
{
    get
    {
        return DateTime.Now.Year - letoRojstva;
    }
    private set
    {
        // Če želimo, da se lastnost Starost nastavi le, ko se ustvari objekt
        letoRojstva = DateTime.Now.Year - value;
    }
}
    

virtual, override in sealed

virtual: označi metodo ali lastnost v osnovnem razredu, ki jo lahko podedovani razred prepiše (override).

override: uporabi se v podrazredu, da nadgradi izvedbo metode ali lastnosti, ki je bila v osnovnem razredu označena kot virtual ali abstract.

sealed: onemogoči nadaljnje prepisovanje metode ali dedovanje razreda. Če označimo metodo kot sealed override, podaran razred ne more te metode več prepisovati. Če označimo razred kot sealed, ga ni mogoče podedovati.

public class Zival
{
    public virtual void NarediZvok()
    {
        Console.WriteLine("Žival izdaja zvok.");
    }
}

public class Pes : Zival
{
    public sealed override void NarediZvok()
    {
        Console.WriteLine("Hov hov!");
    }
}

// Ta poskus bi povzročil napako, ker je Pes razred končan (sealed) pri metodi NarediZvok
public class Nemscina : Pes
{
    public override void NarediZvok()
    {
        Console.WriteLine("Nemški ovčar laja drugače."); // napaka: ni dovoljeno prepisovanje
    }
}
    

Dedovanje (Inheritance)

Dedovanje je mehanizem, s katerim en razred (podrazred ali izpeljan razred) podeduje lastnosti in metode drugega razreda (osnovnega ali nadrazreda). Podrazred lahko razširi, ponovno uporabi ali prepiše člane osnovnega razreda.

public class Vozilo
{
    public int Hitrost { get; set; }
    public void Pospesi(int kmh)
    {
        Hitrost += kmh;
    }
}

public class Avto : Vozilo
{
    public string Model { get; set; }

    public void Predstavi()
    {
        Console.WriteLine($"Model: {Model}, Hitrost: {Hitrost}");
    }
}

// Uporaba:
Avto mojAvto = new Avto();
mojAvto.Model = "Toyota";
mojAvto.Pospesi(50); // metoda iz osnovnega razreda
mojAvto.Predstavi(); // izpiše: Model: Toyota, Hitrost: 50
    

Polimorfizem (Polymorphism)

Polimorfizem omogoča obravnavanje objektov različnih razredov, ki izhajajo iz istega osnovnega razreda ali vmesnika, z eno samo referenco. Pri tem se izvede pravi (dinamični) tip metode ob zagonu.

public class Oblika
{
    public virtual void Risanje()
    {
        Console.WriteLine("Risanje oblike (splošno).");
    }
}

public class Krog : Oblika
{
    public override void Risanje()
    {
        Console.WriteLine("Risanje kroga.");
    }
}

public class Pravokotnik : Oblika
{
    public override void Risanje()
    {
        Console.WriteLine("Risanje pravokotnika.");
    }
}

// Polimorfizem v akciji:
List oblike = new List
{
    new Krog(),
    new Pravokotnik(),
    new Oblika()
};

foreach (var o in oblike)
{
    o.Risanje();
    // Če je tip Krog, izvede Krog.Risanje()
    // Če je tip Pravokotnik, izvede Pravokotnik.Risanje()
    // Drugače izvede Oblika.Risanje()
}
    

Takšen način omogoči, da ena koda obravnava različne izvedbe istih metod, odvisno od dejanskega tipa objekta, ki se hrani v spremenljivki osnovnega tipa.

Pojdi nazaj na vrh

Izjeme in vmesniki

V tem razdelku najprej razložimo, kaj pomenijo izjeme in vmesniki, nato pa podrobno predstavimo posamezne razrede/vmesnike.

Kaj so izjeme (Exceptions)?

Izjeme so mehanizem za obvladovanje napak in nepredvidenih okoliščin med izvajanjem aplikacije. V C# so vse izjeme razredi, ki dedujejo iz System.Exception. Ko se zgodi izjemno stanje (npr. uporabnik je vnesel neveljaven parameter, poskuša prodati preveč steklenic), koda vrže (throw) specifično izjemo. V klicnem bloku (try { … } catch { … }) jo lahko ujamemo in ustrezno obravnavamo:

try
{
    var znesek = mojeVino.Prodaj(100); // poskus prodaje 100 steklenic
}
catch (PrevecSteklenicException ex)
{
    Console.WriteLine($"Napaka pri prodaji: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Nepričakovana napaka: {ex.Message}");
}
        

Poglejmo sedaj razrede, ki so uporabljeni kot izjeme.

Pojdi nazaj na vrh

Kaj so vmesniki (Interfaces)?

Vmesnik (interface) v C# je način, da definiramo "pogodbo" – seznam javnih metod (in/ali lastnosti), ki jih mora razred, ki implementira ta vmesnik, izpolniti. Primerjava:

Prednost uporabe vmesnikov:

Zdaj si oglejmo konkretne razrede/vmesnike.

Pojdi nazaj na vrh

Razred PrevecSteklenicException

Ta izjema se vrže, kadar poskušamo prodati več steklenic, kot jih objekt vsebuje. Deduje iz Exception.

Konstruktorji

public class PrevecSteklenicException : Exception
{
    public PrevecSteklenicException(string message)
        : base(message)
    {
        // Klic osnovnega konstruktorja z opisom napake.
    }
}
        

Izjema se običajno vrže v metodi Prodaj(int kolicinaSteklenic) razreda Vino:

public double Prodaj(int kolicinaSteklenic)
{
    if (kolicinaSteklenic > SteviloSteklenic)
        throw new PrevecSteklenicException(
            $"Ne morete prodati več kot {SteviloSteklenic} steklenic.");
    SteviloSteklenic -= kolicinaSteklenic;
    return CenaNaSteklenico * kolicinaSteklenic;
}
        
Primer: Poskus prodaje prevelike količine
// Predpostavimo, da imamo 5 steklenic
Vino rdece = new Vino("Rdeče vino", 2020, "Cabernet", 13.5, 5, 20.0, "Polno telo");

try
{
    rdece.Prodaj(10); // tukaj je 10 > 5, zato se vrže izjema
}
catch (PrevecSteklenicException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
    // Izhod: Napaka: Ne morete prodati več kot 5 steklenic.
}
            

Pojdi nazaj na vrh

Razred OcenaNeObstajaException

Izjema, ki se vrže, če poskušamo odstraniti oceno z neobstoječim indeksom ali z navedenim ID, ki ne obstaja. Deduje iz Exception.

Konstruktorji

public class OcenaNeObstajaException : Exception
{
    public OcenaNeObstajaException(string message)
        : base(message)
    {
        // Klic osnovnega konstruktorja z opisom napake.
    }
}
        

Razred Vino hrani ocene v List<int> Ocene. Če poskušamo odstraniti oceno, ki ne obstaja, se vrže ta izjema:

public int OdstraniOceno(int idOcene)
{
    if (idOcene < 0 || idOcene >= Ocene.Count)
        throw new OcenaNeObstajaException(
            $"Ocena s podanim indeksom ({idOcene}) ne obstaja.");
    Ocene.RemoveAt(idOcene);
    return Ocene.Count;
}
        
Primer: Poskus odstranitve neobstoječe ocene
Vino belo = new Vino("Belo vino", 2021, "Riesling", 12.0, 10, 15.0, "Sveže in sadno");

// Dodamo eno oceno (indeks 0)
belo.Oceni(5);

// Poskusimo odstraniti oceno z indeksom 3 (ne obstaja)
try
{
    belo.OdstraniOceno(3);
}
catch (OcenaNeObstajaException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
    // Izhod: Napaka: Ocena s podanim indeksom (3) ne obstaja.
}
            

Pojdi nazaj na vrh

Vmesnik IOceni

Vmesnik za objekte, ki jih je mogoče oceniti. Določa tri metode, ki jih mora implementirati vsak razred, ki želi podpirati ocenjevanje (npr. Vino).

Metode

public interface IOceni
{
    /// 
    /// Doda oceno (1–5) in vrne novo število ocen.
    /// Če je ocena izven 1–5, lahko vrže izjemo.
    /// 
    int Oceni(int ocena);

    /// 
    /// Odstrani oceno po indeksu (0-bazirano). Vrne novo število ocen.
    /// Če ocena ne obstaja, vrže OcenaNeObstajaException.
    /// 
    int OdstraniOceno(int idOcene);

    /// 
    /// Izračuna končno ceno po popustu (%) za dano količino steklenic.
    /// 
    double IzracunajKoncnoCeno(double popust, int kolicinaSteklenic);
}
        

Razred Vino implementira ta vmesnik:

public class Vino : IOceni, IProdaja
{
    // Lastnosti...
    public List<int> Ocene { get; set; }

    public int Oceni(int ocena)
    {
        if (ocena < 1 || ocena > 5)
            throw new ArgumentOutOfRangeException(nameof(ocena),
                "Ocena mora biti med 1 in 5.");
        Ocene.Add(ocena);
        return Ocene.Count;
    }

    public int OdstraniOceno(int idOcene)
    {
        if (idOcene < 0 || idOcene >= Ocene.Count)
            throw new OcenaNeObstajaException(
                $"Ocena s podanim indeksom ({idOcene}) ne obstaja.");
        Ocene.RemoveAt(idOcene);
        return Ocene.Count;
    }

    public double IzracunajKoncnoCeno(double popust, int kolicinaSteklenic)
    {
        double original = CenaNaSteklenico * kolicinaSteklenic;
        return original - (original * popust / 100.0);
    }

    // Preostale metode (Prodaj, ToString itd.)...
}
        
Primer uporabe metod vmesnika IOceni na objektu Vino:
// 1. Dodajanje ocene:
Vino cvekla = new Vino("Cvekla", 2019, "Modra Frankinja", 14.5, 20, 18.0, "Polno črno vino.");
int stevilo = cvekla.Oceni(5);        // vrne 1
stevilo = cvekla.Oceni(4);           // vrne 2

// 2. Poskus dodajanja neveljavne ocene:
try
{
    cvekla.Oceni(6); // 6 ni med 1 in 5
}
catch (ArgumentOutOfRangeException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
}

// 3. Izračun cene s popustom:
double cenaPopust = cvekla.IzracunajKoncnoCeno(10.0, 3);
// original = 18.0 * 3 = 54.0
// popust = 10% → 54.0 - 5.4 = 48.6
            

Pojdi nazaj na vrh

Vmesnik IProdaja

Vmesnik za objekte, ki jih je mogoče prodati. Ponuja štiri preobremenjene metode Prodaj. Razredi, ki implementirajo ta vmesnik (kot Vino), morajo poskrbeti za zmanjšanje zaloge in izračun vrednosti prodaje.

Metode

public interface IProdaja
{
    /// 
    /// Proda eno steklenico po polni ceni.
    /// 
    double Prodaj();

    /// 
    /// Proda dano število steklenic po polni ceni.
    /// Če kolicina > SteviloSteklenic, vrže PrevecSteklenicException.
    /// 
    double Prodaj(int kolicinaSteklenic);

    /// 
    /// Proda eno steklenico s podanim popustom (%).
    /// 
    double Prodaj(double popust);

    /// 
    /// Proda dano število steklenic s podanim popustom (%).
    /// Če kolicina > SteviloSteklenic, vrže PrevecSteklenicException.
    /// 
    double Prodaj(double popust, int kolicinaSteklenic);
}
        

Implementacija v razredu Vino:

public class Vino : IOceni, IProdaja
{
    // Lastnosti: SteviloSteklenic, CenaNaSteklenico, ...

    public double Prodaj()
    {
        return Prodaj(1, 0.0); // Privzeta preusmeritev
    }

    public double Prodaj(int kolicinaSteklenic)
    {
        return Prodaj(kolicinaSteklenic, 0.0);
    }

    public double Prodaj(double popust)
    {
        return Prodaj(1, popust);
    }

    public double Prodaj(double popust, int kolicinaSteklenic)
    {
        if (kolicinaSteklenic > SteviloSteklenic)
            throw new PrevecSteklenicException(
                $"Ne morete prodati več kot {SteviloSteklenic} steklenic.");
        double cenaEnotna = CenaNaSteklenico * (1 - popust / 100.0);
        SteviloSteklenic -= kolicinaSteklenic;
        return cenaEnotna * kolicinaSteklenic;
    }

    // Preostale metode (Oceni, OdstraniOceno, ToString itd.)...
}
        
Primer uporabe metod vmesnika IProdaja na objektu Vino:
// Predpostavimo, da imamo 20 steklenic vina z ceno 18 EUR/ste.
Vino cvekla = new Vino("Cvekla", 2019, "Modra Frankinja", 14.5, 20, 18.0, "Polno črno vino.");

// 1. Proda 1 steklenico po polni ceni:
double znesek1 = cvekla.Prodaj();
// zaloga: 20 -> 19, znesek1 = 18.0

// 2. Proda 5 steklenic brez popusta:
double znesek2 = cvekla.Prodaj(5);
// zaloga: 19 -> 14, znesek2 = 5 * 18.0 = 90.0

// 3. Proda 1 steklenico s popustom 15%:
double znesek3 = cvekla.Prodaj(15.0);
// cena enotna = 18.0 * 0.85 = 15.3, zaloga: 14 -> 13

// 4. Proda 3 steklenice s popustom 20%:
double znesek4 = cvekla.Prodaj(20.0, 3);
// original = 18.0 * 3 = 54.0, popust = 20% → 43.2, zaloga: 13 -> 10

// 5. Poskus prodaje več kot je na zalogi:
try
{
    cvekla.Prodaj(50); // 50 > 10 trenutno
}
catch (PrevecSteklenicException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
    // Izhod: Napaka: Ne morete prodati več kot 10 steklenic.
}
            

Pojdi nazaj na vrh

Enumi

Enumi so seznam vnaprej določenih konstant, ki jih uporabimo, kadar želimo zagotoviti, da vrednost spada le v določen nabor možnosti. To preprečuje napake pri vnašanju nizov ali številk "na pamet".

Enum LegaVinograda

Predstavlja možne lege vinogradov. Uporabimo ga, da ne moremo vnesti napačne lege (npr. "Jugozahodna").

public enum LegaVinograda
{
    Juzna,
    Vzhodna,
    Zahodna,
    Severna
}
        

Kadar ustvarjamo nov objekt Vinograd, navedemo lego iz tega seznama:

Vinograd mojVinograd = new Vinograd("Bukov Vinograd", 2.5, LegaVinograda.Vzhodna);
Console.WriteLine($"Lega vinograda: {mojVinograd.Lega}");
// Izpis: Lega vinograda: Vzhodna
        

Pojdi nazaj na vrh

Enum VlogaDelavca

Predstavlja možne vloge delavcev v vinogradništvu. Določa, ali je delavec redno zaposlen ali sezonski.

public enum VlogaDelavca
{
    Zaposlen,
    Sezonski
}
        

Primer uporabe pri kreiranju novega delavca:

Delavec janez = new Delavec("Janez", "Novak", "Srednja šola", VlogaDelavca.Sezonski);
Console.WriteLine($"Delavec Janez je vloga: {janez.Vloga}");
// Izpis: Delavec Janez je vloga: Sezonski
        

Pojdi nazaj na vrh

Razred Vino

Razred Vino predstavlja vino s svojimi ključnimi lastnostmi in omogoča ocenjevanje ter prodajo. Z njim modeliramo entiteto vino v aplikaciji.

Lastnosti (Properties)

Ključne lastnosti razreda Vino:

Opomba: Seznam ocen vedno inicializiramo v konstruktorju, da ni null. Tako lahko preprosto kličemo Ocene.Add(ocena). Dodali smo tudi lastnost Certifikat, da lahko sledimo veljavnosti certifikatov.

Pojdi nazaj na vrh

Konstruktorji

Razred Vino ima tri konstruktorje:

public Vino()
{
    // Privzeti konstruktor
    Ocene = new List<int>();
    Certifikat = null;
}

public Vino(string ime, int letoTrgatve, string sortaGrozdja, double alkoholnaStopnja,
            int steviloSteklenic, double cenaNaSteklenico, string opis)
{
    Ime = ime;
    LetoTrgatve = letoTrgatve;
    SortaGrozdja = sortaGrozdja;
    AlkoholnaStopnja = alkoholnaStopnja;
    SteviloSteklenic = steviloSteklenic;
    CenaNaSteklenico = cenaNaSteklenico;
    Opis = opis;
    Ocene = new List<int>();
    Certifikat = null;
}

public Vino(string ime, int letoTrgatve, string sortaGrozdja, double alkoholnaStopnja,
            int steviloSteklenic, double cenaNaSteklenico, string opis, List<int> ocene, Certifikat certifikat)
{
    Ime = ime;
    LetoTrgatve = letoTrgatve;
    SortaGrozdja = sortaGrozdja;
    AlkoholnaStopnja = alkoholnaStopnja;
    SteviloSteklenic = steviloSteklenic;
    CenaNaSteklenico = cenaNaSteklenico;
    Opis = opis;
    Ocene = ocene ?? new List<int>();
    Certifikat = certifikat;
}
        

Vsak konstruktor poskrbi za:

Primerni načini kreiranja objekta Vino:
// 1. Privzeti konstruktor (popolnoma prazen objekt):
Vino praznoVino = new Vino();
Console.WriteLine(praznoVino.Ocene.Count); // 0

// 2. Konstruktor z osnovnimi parametri (ocene se samodejno nastavi na prazen seznam):
Vino rose = new Vino(
    "Rosé Slikar",
    2022,
    "Pinot Noir",
    13.0,
    30,        // številno steklenic
    12.5,      // cena na steklenico
    "Lahkotno, sadno rosé."
);
Console.WriteLine($"{rose.Ime} ({rose.LetoTrgatve}) → Zaloga: {rose.SteviloSteklenic}");

// 3. Konstruktor z vnaprej navedenimi ocenami in certifikatom:
List<int> oceneSeznam = new List<int> { 5, 4, 3 };
Certifikat cert = new Certifikat(
    "DOC",
    "Slovenska Vinogradniška Zbornica",
    new DateTime(2022, 1, 1),
    new DateTime(2024, 12, 31)
);
Vino posebnoVino = new Vino(
    "Chardonnay Deluxe",
    2021,
    "Chardonnay",
    14.5,
    15,
    22.0,
    "Polno telo, hrastna zorenje",
    oceneSeznam,
    cert
);
Console.WriteLine($"Št. ocen: {posebnoVino.Ocene.Count}, Certifikat veljaven do: {posebnoVino.Certifikat.DatumVeljavnosti:dd.MM.yyyy}"); // 3, 31.12.2024
            

Pojdi nazaj na vrh

Metode

Razred Vino vsebuje naslednje javne metode:

int Oceni(int ocena);
int OdstraniOceno(int idOcene);
double IzracunajKoncnoCeno(double popust, int kolicinaSteklenic);
double Prodaj();
double Prodaj(int kolicinaSteklenic);
double Prodaj(double popust);
double Prodaj(double popust, int kolicinaSteklenic);
override string ToString();
        

1. Oceni(int ocena)

Ta metoda doda oceno (1–5) v seznam ocen in vrne novo število ocen. Če je ocena zunaj dovoljenega razpona, vrže ArgumentOutOfRangeException.

public int Oceni(int ocena)
{
    if (ocena < 1 || ocena > 5)
        throw new ArgumentOutOfRangeException(nameof(ocena),
            "Ocena mora biti med 1 in 5.");
    Ocene.Add(ocena);
    return Ocene.Count;
}
        
Primer uporabe metode Oceni:
Vino cvekla = new Vino("Cvekla", 2019, "Modra Frankinja", 14.5, 20, 18.0, "Polno črno vino.");
int st1 = cvekla.Oceni(5); // vrne 1
int st2 = cvekla.Oceni(4); // vrne 2

try
{
    cvekla.Oceni(6); // 6 ni dovoljena ocena
}
catch (ArgumentOutOfRangeException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
}
// Izhod: Napaka: Ocena mora biti med 1 in 5.
            

2. OdstraniOceno(int idOcene)

Odstrani oceno po indeksu (0-bazirano). Če indeks ne obstaja, vrže OcenaNeObstajaException.

public int OdstraniOceno(int idOcene)
{
    if (idOcene < 0 || idOcene >= Ocene.Count)
        throw new OcenaNeObstajaException(
            $"Ocena s podanim indeksom ({idOcene}) ne obstaja.");
    Ocene.RemoveAt(idOcene);
    return Ocene.Count;
}
        
Primer uporabe metode OdstraniOceno:
Vino belo = new Vino("Belo vino", 2021, "Riesling", 12.0, 10, 15.0, "Sveže in sadno");
belo.Oceni(5); // lista ocen: [5]
int st = belo.OdstraniOceno(0); // vrne 0, ker je zdaj lista prazna

try
{
    belo.OdstraniOceno(2); // indeks 2 ne obstaja
}
catch (OcenaNeObstajaException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
}
// Izhod: Napaka: Ocena s podanim indeksom (2) ne obstaja.
            

3. IzracunajKoncnoCeno(double popust, int kolicinaSteklenic)

Izračuna skupno ceno s popustom za dano količino steklenic. Originalna cena = CenaNaSteklenico * kolicinaSteklenic. Popust (%) se odšteje od te vsote.

public double IzracunajKoncnoCeno(double popust, int kolicinaSteklenic)
{
    double original = CenaNaSteklenico * kolicinaSteklenic;
    return original - (original * popust / 100.0);
}
        
Primer izračuna cene s popustom:
Vino cvekla = new Vino("Cvekla", 2019, "Modra Frankinja", 14.5, 20, 18.0, "Polno črno vino.");
double cena1 = cvekla.IzracunajKoncnoCeno(10.0, 3);
// 3 * 18.0 = 54.0, 10% popust: 54.0 - 5.4 = 48.6

Console.WriteLine($"Končna cena: {cena1} €"); // Izpis: Končna cena: 48.6 €
            

4. Prodaj() in preobremenjene različice Prodaj(...)

Metode za prodajo zmanjšujejo zalogo (SteviloSteklenic) in vračajo skupni znesek prodaje. Obstajajo štiri različice:

public double Prodaj()                    // Proda 1 steklenico brez popusta
public double Prodaj(int kolicinaSteklenic)                    // Proda podano število brez popusta
public double Prodaj(double popust)                                // Proda 1 steklenico s popustom (%)
public double Prodaj(double popust, int kolicinaSteklenic)         // Proda podano število s popustom (%)
        

V ozadju vse izvedbe uporabljajo zadnjo različico:

public double Prodaj(double popust, int kolicinaSteklenic)
{
    if (kolicinaSteklenic > SteviloSteklenic)
        throw new PrevecSteklenicException(
            $"Ne morete prodati več kot {SteviloSteklenic} steklenic.");
    double cenaEnotna = CenaNaSteklenico * (1 - popust / 100.0);
    SteviloSteklenic -= kolicinaSteklenic;
    return cenaEnotna * kolicinaSteklenic;
}
        
Primeri uporabe metode Prodaj:
// Imamo 20 steklenic vina po 18 €/ste:
Vino cvekla = new Vino("Cvekla", 2019, "Modra Frankinja", 14.5, 20, 18.0, "Polno črno vino.");

// Proda 1 steklenico po polni ceni:
double z1 = cvekla.Prodaj();
// zaloga: 20 -> 19, z1 = 18.0

// Proda 5 steklenic brez popusta:
double z2 = cvekla.Prodaj(5);
// zaloga: 19 -> 14, z2 = 5 * 18.0 = 90.0

// Proda 1 steklenico s popustom 15%:
double z3 = cvekla.Prodaj(15.0);
// enotna cena = 18 * 0.85 = 15.3, zaloga: 14 -> 13

// Proda 3 steklenice s popustom 20%:
double z4 = cvekla.Prodaj(20.0, 3);
// original = 3 * 18 = 54, popust20% = 43.2, zaloga: 13 -> 10

// Poskus prodaje več kot je na zalogi:
try
{
    cvekla.Prodaj(50); // 50 > 10 trenutno
}
catch (PrevecSteklenicException ex)
{
    Console.WriteLine($"Napaka: {ex.Message}");
    // Izhod: Napaka: Ne morete prodati več kot 10 steklenic.
}
            

5. ToString()

Metoda ToString() vrne niz, ki opisuje stanje objekta Vino. To je uporabno, kadar želimo povzeti vse ključne informacije v enem nizu:

public override string ToString()
{
    return $"{Ime} ({LetoTrgatve}) - Sorta: {SortaGrozdja}, "
         + $"Zaloga: {SteviloSteklenic}, Cena: {CenaNaSteklenico} €, "
         + $"Certifikat veljaven do: {(Certifikat != null ? Certifikat.DatumVeljavnosti.ToString("dd.MM.yyyy") : "Brez certifikata")}";
}
        
Primer uporabe ToString():
Vino rdece = new Vino("Rdeče vino", 2020, "Cabernet", 13.5, 5, 20.0, "Polno telo");
Console.WriteLine(rdece.ToString());
// Izpis: Rdeče vino (2020) - Sorta: Cabernet, Zaloga: 5, Cena: 20 €, Certifikat veljaven do: Brez certifikata

// Če ima certifikat:
Certifikat cert = new Certifikat("DOC", "Slovenska Vinogradniška Zbornica", new DateTime(2020,1,1), new DateTime(2023,12,31));
Vino belo = new Vino("Belo vino", 2019, "Riesling", 12.0, 10, 15.0, "Sveže in sadno", new List<int>(), cert);
Console.WriteLine(belo.ToString());
// Izpis: Belo vino (2019) - Sorta: Riesling, Zaloga: 10, Cena: 15 €, Certifikat veljaven do: 31.12.2023
            

Pojdi nazaj na vrh

Razred Delavec

Razred Delavec predstavlja vinogradnega delavca z imenom, priimkom, izobrazbo, vlogo, razpoložljivostjo in zneskom, ki ga je delavec zaslužil.

Lastnosti (Properties)

Ključne lastnosti razreda Delavec:

Opomba: Lastnost Zasluzi se posodablja prek metode DodajZasluzek ali se ponastavi prek metode Izplacaj.

Pojdi nazaj na vrh

Konstruktorji

Razred Delavec ima tri konstruktorje:

public Delavec()
{
    // Privzeti konstruktor
    Razpolozljiv = true;
    Zasluzi = 0.0;
}

public Delavec(string ime, string priimek, string izobrazba, VlogaDelavca vloga)
{
    Ime = ime;
    Priimek = priimek;
    Izobrazba = izobrazba;
    Vloga = vloga;
    Razpolozljiv = true;
    Zasluzi = 0.0;
}

public Delavec(Delavec original)
{
    Ime = original.Ime;
    Priimek = original.Priimek;
    Izobrazba = original.Izobrazba;
    Vloga = original.Vloga;
    Razpolozljiv = original.Razpolozljiv;
    Zasluzi = original.Zasluzi;
}
        

Opomba o konstruktorjih:

Primeri kreiranja objektov Delavec:
// 1. Privzeti konstruktor:
Delavec d1 = new Delavec();
Console.WriteLine($"Razpoložljiv: {d1.Razpolozljiv}, Zasluži: {d1.Zasluzi}");
// Izhod: Razpoložljiv: True, Zasluži: 0

// 2. Konstruktor z argumenti:
Delavec d2 = new Delavec("Ana", "Kraner", "Diplomirani inženir", VlogaDelavca.Zaposlen);
Console.WriteLine($"{d2.Ime} {d2.Priimek}, Izobrazba: {d2.Izobrazba}, Vloga: {d2.Vloga}");
// Izhod: Ana Kraner, Izobrazba: Diplomirani inženir, Vloga: Zaposlen

// 3. Kopirni konstruktor:
Delavec d3 = new Delavec(d2);
Console.WriteLine($"Kopija: {d3.Ime} {d3.Priimek}, Zasluži: {d3.Zasluzi}");
// Izhod: Kopija: Ana Kraner, Zasluži: 0
            

Pojdi nazaj na vrh

Metode

Razred Delavec vsebuje naslednje javne metode:

void Izplacaj(Delavec d);
void DodajZasluzek(Delavec d, double znesek);
override string ToString();
        

1. DodajZasluzek(Delavec d, double znesek)

Ta metoda doda znesek k lastnosti d.Zasluzi. Če delavca d ni mogoče najti v kontekstu, se lahko vrže izjema (ni prikazano).

public void DodajZasluzek(Delavec d, double znesek)
{
    // Predpostavimo, da smo v razredu Vinogradnistvo
    // in iščemo d v seznamu SeznamDelavcev.
    d.Zasluzi += znesek;
}
        
Primer uporabe DodajZasluzek:
Delavec toni = new Delavec("Toni", "Zupan", "Srednja šola", VlogaDelavca.Sezonski);
toni.DodajZasluzek(toni, 200.0); // Toni.Zasluzi = 200
Console.WriteLine($"Toni zasluži: {toni.Zasluzi} EUR");
// Izhod: Toni zasluži: 200 EUR
            

2. Izplacaj(Delavec d)

Izplača delavca d – to pomeni posredovanje celotnega zneska d.Zasluzi in ponastavitev d.Zasluzi = 0. Lahko tudi spremeni d.Razpolozljiv = false, če delavca umaknemo iz razpoložljive baze (odvisno od implementacije).

public void Izplacaj(Delavec d)
{
    if (!d.Razpolozljiv)
        throw new InvalidOperationException("Delavec ni razpoložljiv za izplačilo.");
    double znesek = d.Zasluzi;
    // Tukaj bi posredovali znesek na bančni račun ali vplačali izplačilo
    d.Zasluzi = 0.0;
    d.Razpolozljiv = false;
}
        
Primer uporabe Izplacaj:
Delavec liza = new Delavec("Liza", "Novak", "Diplomiran ekonomist", VlogaDelavca.Zaposlen);
liza.DodajZasluzek(liza, 300.0); // liza.Zasluzi = 300
Console.WriteLine($"Pred izplačilom: {liza.Zasluzi} EUR");
// Izhod: Pred izplačilom: 300 EUR

liza.Izplacaj(liza);
Console.WriteLine($"Po izplačilu: {liza.Zasluzi} EUR, Razpoložljiv: {liza.Razpolozljiv}");
// Izhod: Po izplačilu: 0 EUR, Razpoložljiv: False
            

3. ToString()

Vrne nizni opis stanja delavca:

public override string ToString()
{
    return $"{Ime} {Priimek} - Vloga: {Vloga}, Razpoložljiv: {Razpolozljiv}, Zasluži: {Zasluzi:F2} EUR";
}
        
Primer uporabe ToString():
Delavec eva = new Delavec("Eva", "Kos", "Diplomiran ekonomist", VlogaDelavca.Zaposlen);
eva.DodajZasluzek(eva, 150.0);
Console.WriteLine(eva.ToString());
// Izhod: Eva Kos - Vloga: Zaposlen, Razpoložljiv: True, Zasluži: 150.00 EUR
            

Pojdi nazaj na vrh

Razred Trgatev

Razred Trgatev modelira dogodek trgatve grozdja: datum, sorta, količino, sladkorno stopnjo in seznam delavcev, ki sodelujejo.

Lastnosti (Properties)

Ključne lastnosti razreda Trgatev:

Opomba: Seznam Udelezenci vedno inicializiramo v konstruktorju kot prazen seznam, da ne pride do null reference.

Pojdi nazaj na vrh

Konstruktorji

Razred Trgatev ima štiri konstruktorje:

public Trgatev()
{
    Udelezenci = new List<Delavec>();
    SladkornaStopnja = 0.0;
}

public Trgatev(DateTime datumTrgatve, string sortaGrozdja, double kolicinaGrozdjaKg)
    : this(datumTrgatve, sortaGrozdja, kolicinaGrozdjaKg, 0.0, new List<Delavec>())
{ }

public Trgatev(DateTime datumTrgatve, string sortaGrozdja, double kolicinaGrozdjaKg,
               double sladkornaStopnja, List<Delavec> udelezenci)
{
    DatumTrgatve = datumTrgatve;
    SortaGrozdja = sortaGrozdja;
    KolicinaGrozdjaKg = kolicinaGrozdjaKg;
    SladkornaStopnja = sladkornaStopnja;
    Udelezenci = udelezenci;
}

public Trgatev(Trgatev original)
{
    DatumTrgatve = original.DatumTrgatve;
    SortaGrozdja = original.SortaGrozdja;
    KolicinaGrozdjaKg = original.KolicinaGrozdjaKg;
    SladkornaStopnja = original.SladkornaStopnja;
    Udelezenci = new List<Delavec>(original.Udelezenci);
}
        

Opis konstruktorjev:

Primeri kreiranja trgatve:
// 1. Privzeti konstruktor:
Trgatev prazna = new Trgatev();
Console.WriteLine($"Št. sodelujočih: {prazna.Udelezenci.Count}"); // 0

// 2. Konstruktor z osnovnimi parametri:
Trgatev majTrgatev = new Trgatev(
    new DateTime(2023, 9, 15),
    "Chardonnay",
    1200.0  // kg
);
// SladkornaStopnja = 0.0, Udelezenci = prazen seznam

// 3. Konstruktor s polnimi parametri:
List<Delavec> ekipa = new List<Delavec>
{
    new Delavec("Bojan", "Kralj", "Visoka šola", VlogaDelavca.Sezonski),
    new Delavec("Maja", "Horvat", "Srednja šola", VlogaDelavca.Sezonski)
};
Trgatev polnaTrgatev = new Trgatev(
    new DateTime(2024, 10, 1),
    "Merlot",
    800.0,
    22.5,    // °Brix
    ekipa
);
// Izpis podrobnosti:
Console.WriteLine($"Datum: {polnaTrgatev.DatumTrgatve:dd.MM.yyyy}, Sorta: {polnaTrgatev.SortaGrozdja}, Udeleženci: {polnaTrgatev.Udelezenci.Count}");

// 4. Kopirni konstruktor:
Trgatev kopija = new Trgatev(polnaTrgatev);
Console.WriteLine($"Sladkor: {kopija.SladkornaStopnja} °Brix"); // 22.5
            

Pojdi nazaj na vrh

Metode

Razred Trgatev vsebuje le eno javno metodo:

public override string ToString();
        

Metoda ToString() vrne strnjen opis trgatve, vključno z datumom, sorto, količino, sladkorno stopnjo in številom udeležencev.

Primer implementacije in uporabe ToString():
public override string ToString()
{
    return $"Trgatev {DatumTrgatve:dd.MM.yyyy}, {SortaGrozdja}, {KolicinaGrozdjaKg} kg, "
         + $"{SladkornaStopnja} °Brix, {Udelezenci.Count} delavcev";
}

// Uporaba:
Console.WriteLine(polnaTrgatev.ToString());
// Izhod: Trgatev 01.10.2024, Merlot, 800 kg, 22.5 °Brix, 2 delavcev
            

Pojdi nazaj na vrh

Razred Vinograd

Razred Vinograd predstavlja fizični vinograd z imenom, površino, sortami grozdja, številom trsov, lego in seznamom trgatev.

Lastnosti (Properties)

Ključne lastnosti razreda Vinograd:

Opomba: Seznama SorteGrozdja in SeznamTrgatev vedno inicializiramo kot novi seznami v konstruktorju, da preprečimo null reference.

Pojdi nazaj na vrh

Konstruktorji

Razred Vinograd ima tri konstruktorje:

public Vinograd()
{
    SorteGrozdja = new List<string>();
    SeznamTrgatev = new List<Trgatev>();
    SteviloTrsov = 0;
}

public Vinograd(string imeVinograda, double povrsinaHa, LegaVinograda lega)
    : this(imeVinograda, povrsinaHa, new List<string>(), 0, lega, new List<Trgatev>())
{ }

public Vinograd(string imeVinograda, double povrsinaHa, List<string> sorteGrozdja,
               int steviloTrsov, LegaVinograda lega, List<Trgatev> seznamTrgatev)
{
    ImeVinograda = imeVinograda;
    PovrsinaHa = povrsinaHa;
    SorteGrozdja = sorteGrozdja;
    SteviloTrsov = steviloTrsov;
    Lega = lega;
    SeznamTrgatev = seznamTrgatev;
}
        

Opis konstruktorjev:

Primeri kreiranja objektov Vinograd:
// 1. Privzeti konstruktor:
Vinograd prazenVinograd = new Vinograd();
Console.WriteLine($"Št. sort: {prazenVinograd.SorteGrozdja.Count}, Št. trgatev: {prazenVinograd.SeznamTrgatev.Count}");
// Izhod: Št. sort: 0, Št. trgatev: 0

// 2. Konstruktor z ime, površina, lega:
Vinograd apelVinograd = new Vinograd("Apel Vinograd", 1.2, LegaVinograda.Juzna);
Console.WriteLine($"{apelVinograd.ImeVinograda}, Površina: {apelVinograd.PovrsinaHa} ha, Lega: {apelVinograd.Lega}");
// Izhod: Apel Vinograd, Površina: 1.2 ha, Lega: Juzna

// 3. Konstruktor s polnimi parametri:
List<string> sorte = new List<string> { "Chardonnay", "Merlot" };
List<Trgatev> trgatevList = new List<Trgatev>
{
    new Trgatev(new DateTime(2023, 9, 20), "Chardonnay", 1000.0),
    new Trgatev(new DateTime(2023, 10, 5), "Merlot", 800.0)
};
Vinograd polniVinograd = new Vinograd(
    "Polni Vinograd",
    3.5,
    sorte,
    1500,               // št. trsov
    LegaVinograda.Zahodna,
    trgatevList
);
Console.WriteLine($"{polniVinograd.ImeVinograda}, Sorte: {string.Join(", ", polniVinograd.SorteGrozdja)}, Št. trgatev: {polniVinograd.SeznamTrgatev.Count}");
// Izhod: Polni Vinograd, Sorte: Chardonnay, Merlot, Št. trgatev: 2
            

Pojdi nazaj na vrh

Metode

Razred Vinograd ima eno glavno javno metodo:

public override string ToString();
        

Metoda ToString() vrne strnjen opis vinograda, vključno z imenom, površino, lego in številom trgatev.

Primer implementacije in uporabe ToString():
public override string ToString()
{
    return $"{ImeVinograda} ({PovrsinaHa} ha, {Lega}), {SeznamTrgatev.Count} trgatev";
}

// Uporaba:
Console.WriteLine(polniVinograd.ToString());
// Izhod: Polni Vinograd (3.5 ha, Zahodna), 2 trgatev
            

Pojdi nazaj na vrh

Razred Certifikat

Razred Certifikat modelira certifikate, ki se podelijo vinom ali vinogradom. Vključuje standard, regijo, oceno kakovosti in datume.

Lastnosti (Properties)

Ključne lastnosti razreda Certifikat:

Opomba: Če izpustimo datume, jih lahko nastavimo na trenutni čas (DateTime.Now) v privzetem konstruktorju.

Pojdi nazaj na vrh

Konstruktorji

Razred Certifikat ima dva konstruktorja:

public Certifikat()
{
    OcenaKakovosti = 0;
    DatumIzdaje = DateTime.Now;
    DatumVeljavnosti = DateTime.Now;
}

public Certifikat(string naziv, string izdajatelj, DateTime datumIzdaje, DateTime datumVeljavnosti)
{
    Standard = naziv;
    Regija = izdajatelj;
    OcenaKakovosti = 0;           // privzeta ocena, se lahko nastavi kasneje
    DatumIzdaje = datumIzdaje;
    DatumVeljavnosti = datumVeljavnosti;
}
        

Opis konstruktorjev:

Primeri kreiranja objekta Certifikat:
// 1. Privzeti konstruktor:
Certifikat osnovniCert = new Certifikat();
Console.WriteLine($"Standard: {osnovniCert.Standard}, Datum izdaje: {osnovniCert.DatumIzdaje}");
// Izpis: Standard: , Datum izdaje: (trenutni datum in čas)

// 2. Konstruktor z argumenti:
Certifikat bioCert = new Certifikat(
    "BIO",
    "Slovenska Vinogradniška Zbornica",
    new DateTime(2023, 1, 1),
    new DateTime(2025, 12, 31)
);
Console.WriteLine($"Standard: {bioCert.Standard}, Veljaven do: {bioCert.DatumVeljavnosti:dd.MM.yyyy}");
// Izhod: Standard: BIO, Veljaven do: 31.12.2025
            

Pojdi nazaj na vrh

Metode

Razred Certifikat vsebuje dve prepisani metodi:

sealed override string ToString();
override DateTime PodaljsajCertifikat();
        

1. ToString()

Vrne povzetek certifikata, vključno s standardom, regijo, datumom veljavnosti in oceno kakovosti.

public sealed override string ToString()
{
    return $"Certifikat {Standard}, Regija: {Regija}, Veljavnost: {DatumVeljavnosti:dd.MM.yyyy}, Ocena: {OcenaKakovosti}";
}
        
Primer uporabe ToString():
Console.WriteLine(bioCert.ToString());
// Izpis: Certifikat BIO, Regija: Slovenska Vinogradniška Zbornica, Veljavnost: 31.12.2025, Ocena: 0
            

2. PodaljsajCertifikat()

Podaljša DatumVeljavnosti za eno leto (lahko priredimo logiko po potrebi). Vrne novi datum veljavnosti.

public override DateTime PodaljsajCertifikat()
{
    DatumVeljavnosti = DatumVeljavnosti.AddYears(1);
    return DatumVeljavnosti;
}
        
Primer uporabe PodaljsajCertifikat():
DateTime novaVelj = bioCert.PodaljsajCertifikat();
Console.WriteLine($"Nova veljavnost: {novaVelj:dd.MM.yyyy}");
// Izpis: Nova veljavnost: 31.12.2026
            

Pojdi nazaj na vrh

Razred Vinogradnistvo

Razred Vinogradnistvo upravlja z zbirkami vin, vinogradov in delavcev. Z njim lahko dodajamo nove elemente, izračunavamo skupno vrednost, dodajamo zaslužke delavcem in jih izplačujemo.

Lastnosti (Properties)

Razred Vinogradnistvo vsebuje tri glavne lastnosti (seznami):

Opomba: vse tri zbirke inicializiramo kot novi prazni seznami v konstruktorju, da ne pride do null napak.

Pojdi nazaj na vrh

Konstruktorji

Razred Vinogradnistvo ima tri konstruktorje:

public Vinogradnistvo()
{
    SeznamVin = new List<Vino>();
    SeznamVinogradov = new List<Vinograd>();
    SeznamDelavcev = new List<Delavec>();
}

public Vinogradnistvo(List<Vino> sezVin, List<Vinograd> sezVinogradov, List<Delavec> sezDelavcev)
{
    SeznamVin = sezVin;
    SeznamVinogradov = sezVinogradov;
    SeznamDelavcev = sezDelavcev;
}

public Vinogradnistvo(Vinogradnistvo original)
{
    SeznamVin = new List<Vino>(original.SeznamVin);
    SeznamVinogradov = new List<Vinograd>(original.SeznamVinogradov);
    SeznamDelavcev = new List<Delavec>(original.SeznamDelavcev);
}
        

Opis konstruktorjev:

Primeri kreiranja objekta Vinogradnistvo:
// 1. Privzeti konstruktor:
Vinogradnistvo prazno = new Vinogradnistvo();
Console.WriteLine($"Vina: {prazno.SeznamVin.Count}, Vinogradov: {prazno.SeznamVinogradov.Count}, Delavcev: {prazno.SeznamDelavcev.Count}");
// Izhod: Vina: 0, Vinogradov: 0, Delavcev: 0

// 2. Konstruktor z že obstoječimi seznami:
List<Vino> listaVin = new List<Vino>
{
    new Vino("Modri Portugal", 2020, "Modri Portugal", 12.5, 50, 10.0, "Ljudsko vino"),
    new Vino("Chardonnay Special", 2022, "Chardonnay", 13.5, 25, 18.0, "Polno vino")
};

List<Vinograd> listaVinogradov = new List<Vinograd>
{
    new Vinograd("Glavni Vinograd", 4.0, LegaVinograda.Severna),
    new Vinograd("Mali Vinograd", 1.5, LegaVinograda.Vzhodna)
};

List<Delavec> listaDelavcev = new List<Delavec>
{
    new Delavec("Luka", "Markič", "Srednja šola", VlogaDelavca.Zaposlen),
    new Delavec("Eva", "Kos", "Diplomiran ekonomist", VlogaDelavca.Zaposlen)
};

Vinogradnistvo mojeVinogradnistvo = new Vinogradnistvo(listaVin, listaVinogradov, listaDelavcev);
Console.WriteLine($"Vina: {mojeVinogradnistvo.SeznamVin.Count}, Vinogradov: {mojeVinogradnistvo.SeznamVinogradov.Count}, Delavcev: {mojeVinogradnistvo.SeznamDelavcev.Count}");
// Izhod: Vina: 2, Vinogradov: 2, Delavcev: 2

// 3. Kopirni konstruktor:
Vinogradnistvo kopija = new Vinogradnistvo(mojeVinogradnistvo);
Console.WriteLine($"Kopija - Vina: {kopija.SeznamVin.Count}, Vinogradov: {kopija.SeznamVinogradov.Count}, Delavcev: {kopija.SeznamDelavcev.Count}");
// Izpis: Kopija - Vina: 2, Vinogradov: 2, Delavcev: 2
            

Pojdi nazaj na vrh

Metode

Razred Vinogradnistvo vsebuje naslednje javne metode:

void DodajZasluzek(Delavec d, double znesek);
void Izplacaj(Delavec d);
override string ToString();
        

1. DodajZasluzek(Delavec d, double znesek)

Ta metoda poišče delavca d v seznamu SeznamDelavcev in mu doda znesek k d.Zasluzi. Če delavca ni v seznamu, lahko vrže izjemo (ni prikazano) ali preprosto ne naredi nič (odvisno od odločitve razvoja).

public void DodajZasluzek(Delavec d, double znesek)
{
    var najden = SeznamDelavcev.FirstOrDefault(x => x.Ime == d.Ime && x.Priimek == d.Priimek);
    if (najden == null)
        throw new InvalidOperationException("Delavec ne obstaja v seznamu.");
    najden.Zasluzi += znesek;
}
        
Primer uporabe DodajZasluzek:
// Predpostavimo, da sta dva delavca že v SeznamDelavcev:
Delavec liza = mojeVinogradnistvo.SeznamDelavcev[1]; // "Eva Kos"
mojeVinogradnistvo.DodajZasluzek(liza, 300.0);
Console.WriteLine($"Eva zasluži: {liza.Zasluzi} EUR");
// Izhod: Eva zasluži: 300 EUR
            

2. Izplacaj(Delavec d)

Izplača delavca d, kar pomeni, da prenese vso vrednost d.Zasluzi v zunanjo transakcijo, dodeli d.Zasluzi = 0 in lahko spremeni d.Razpolozljiv = false.

public void Izplacaj(Delavec d)
{
    var najden = SeznamDelavcev.FirstOrDefault(x => x.Ime == d.Ime && x.Priimek == d.Priimek);
    if (najden == null)
        throw new InvalidOperationException("Delavec ne obstaja v seznamu.");
    if (!najden.Razpolozljiv)
        throw new InvalidOperationException("Delavec ni razpoložljiv za izplačilo.");
    double znesek = najden.Zasluzi;
    // Tu bi posredovali denar na račun delavca …
    najden.Zasluzi = 0.0;
    najden.Razpolozljiv = false;
}
        
Primer uporabe Izplacaj:
Delavec eva = mojeVinogradnistvo.SeznamDelavcev[1]; // "Eva Kos"
mojeVinogradnistvo.DodajZasluzek(eva, 300.0);
Console.WriteLine($"Pred izplačilom: {eva.Zasluzi}");
// Izhod: Pred izplačilom: 300

mojeVinogradnistvo.Izplacaj(eva);
Console.WriteLine($"Po izplačilu: {eva.Zasluzi}, Razpoložljiv: {eva.Razpoložljiv}");
// Izhod: Po izplačilu: 0, Razpoložljiv: False
            

3. ToString()

Vrne povzetek stanja celotnega Vinogradnistvo:

public override string ToString()
{
    return $"Vinogradništvo: {SeznamVin.Count} vino, {SeznamVinogradov.Count} vinograd, {SeznamDelavcev.Count} delavci";
}
        
Primer uporabe ToString():
Console.WriteLine(mojeVinogradnistvo.ToString());
// Izhod: Vinogradništvo: 2 vino, 2 vinograd, 2 delavci
            

Pojdi nazaj na vrh

Razred ZejniStajerc

Razred ZejniStajerc skrbi za branje in pisanje podatkov (vin, vinogradov, delavcev, trgatev) iz/na zunanji vir (CSV datoteke). Omogoča tudi prijavo dogodekov za uspešno/neuspešno branje in pisanje.

Dogodki in delegati

Razred ZejniStajerc definira delegat in eno ali več dogodkov, ki se sprožijo ob uspehu ali neuspehu operacije branja/pisanja. To omogoča, da se drugi deli aplikacije (npr. UI) lahko prijavijo (subscribe) in reagirajo:

public delegate void Obvestilo(string sporocilo);

public static event Obvestilo UspesnoBranje;
public static event Obvestilo NeuspesnoBranje;
public static event Obvestilo UspesnoPisanje;
public static event Obvestilo NeuspesnoPisanje;
        

Ob prijavi dogodka izvajamo kodo, ki npr. prikaže obvestilo uporabniku ali zapiše v log datoteko.

Primer prijave na dogodke:
// Prijavimo se na dogodke:
ZejniStajerc.UspesnoBranje += (msg) => Console.WriteLine($"OK: {msg}");
ZejniStajerc.NeuspesnoBranje += (msg) => Console.WriteLine($"Napaka pri branju: {msg}");
ZejniStajerc.UspesnoPisanje += (msg) => Console.WriteLine($"Uspešno shranjeno: {msg}");
ZejniStajerc.NeuspesnoPisanje += (msg) => Console.WriteLine($"Napaka pri pisanju: {msg}");
            

Pojdi nazaj na vrh

Metode za branje/pisanje

Razred ZejniStajerc ima več statičnih metod za branje in pisanje CSV datotek:

static void LogException(Exception ex, string opis);
static List<Vino> ReadVinoCsv(string potDoDatoteke);
static List<Vinograd> ReadVinogradCsv(string potDoDatoteke);
static List<Delavec> ReadDelavecCsv(string potDoDatoteke);
static List<Trgatev> ReadTrgatevCsv(string potDoDatoteke);
static void WriteVinoCsv(string potDoDatoteke, List<Vino> vina);
static void WriteVinogradCsv(string potDoDatoteke, List<Vinograd> vinogradi);
static void WriteDelavecCsv(string potDoDatoteke, List<Delavec> delavci);
static void WriteTrgatevCsv(string potDoDatoteke, List<Trgatev> trgateve);
        

Opis metod:

Primer branja in pisanja CSV podatkov:
// Branje seznama vin iz CSV:
try
{
    var vina = ZejniStajerc.ReadVinoCsv("vina.csv");
    Console.WriteLine($"Prebrana vina: {vina.Count}");
}
catch (Exception ex)
{
    // Dogodek NeuspesnoBranje je že sprožen znotraj ReadVinoCsv
    Console.WriteLine($"Napaka pri branju vin: {ex.Message}");
}

// Pisanje seznama vin v CSV:
List<Vino> mojaVinskaKolekcija = new List<Vino> { rose, cvekla };
ZejniStajerc.WriteVinoCsv("vina_izpis.csv", mojaVinskaKolekcija);
// Če je zapis uspešen, se sproži dogodek UspesnoPisanje
            

Pojdi nazaj na vrh

Metoda Main (osnovni zagon)

Metoda Main je vhodna točka konzolne aplikacije. Tu običajno:

  1. Prijavimo se na dogodke (UspesnoBranje, NeuspesnoBranje, UspesnoPisanje, NeuspesnoPisanje).
  2. Preberemo vse potrebne podatke (vina, vinograde, delavce, trgatev) iz CSV datotek.
  3. Izvedemo poslovno logiko (prodaja, ocenjevanje, poročila) ali prikažemo meni uporabniku.
  4. Shranimo končne spremembe nazaj v CSV datoteke.
static void Main(string[] args)
{
    // 1. Prijava dogodkov
    ZejniStajerc.UspesnoBranje += (msg) => Console.WriteLine($"OK: {msg}");
    ZejniStajerc.NeuspesnoBranje += (msg) => Console.WriteLine($"Napaka pri branju: {msg}");
    ZejniStajerc.UspesnoPisanje += (msg) => Console.WriteLine($"Uspešno shranjeno: {msg}");
    ZejniStajerc.NeuspesnoPisanje += (msg) => Console.WriteLine($"Napaka pri pisanju: {msg}");

    // 2. Branje podatkov iz CSV
    try
    {
        var vina = ZejniStajerc.ReadVinoCsv("vina.csv");
        var vinogradi = ZejniStajerc.ReadVinogradCsv("vinogradi.csv");
        var delavci = ZejniStajerc.ReadDelavecCsv("delavci.csv");
        var trgateve = ZejniStajerc.ReadTrgatevCsv("trgatev.csv");

        // Ustvarimo instanco Vinogradnistvo z prebranimi podatki
        Vinogradnistvo mojeVinogradništvo = new Vinogradnistvo(vina, vinogradi, delavci);

        // 3. Preprosto izpis stanja
        Console.WriteLine(mojeVinogradništvo.ToString());

        // 4. Po končani uporabi shranimo spremembe v nove CSV datoteke
        ZejniStajerc.WriteVinoCsv("vina_izpis.csv", mojeVinogradništvo.SeznamVin);
        ZejniStajerc.WriteVinogradCsv("vinogradi_izpis.csv", mojeVinogradništvo.SeznamVinogradov);
        ZejniStajerc.WriteDelavecCsv("delavci_izpis.csv", mojeVinogradništvo.SeznamDelavcev);
        ZejniStajerc.WriteTrgatevCsv("trgatev_izpis.csv", trgateve);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Nepričakovana napaka v Main: {ex.Message}");
    }
}
        

S tem zagotovimo strukturo tipične konzolne aplikacije s C# in CSV vmesnikom.

Pojdi nazaj na vrh

Obdelava podatkov z uporabo Lambda izrazov in LINQ

V tem razdelku prikažemo primere, kako obdelovati obstoječe sezname vin, trgatev in delavcev s pomočjo LINQ-poizvedb (lambda sintaksa). Upravljamo s CSV podatki, ki so že naloženi v sezname objektov (List<Vino>, List<Trgatev>, List<Delavec>, List<Certifikat>).

a) Filtriranje vin s poteklim certifikatom

Najprej predpostavimo, da imamo naložen seznam List<Vino> seznamVin in da je vsak Vino objekt povezan z lastnostjo Certifikat. Ta certifikat vsebuje lastnost DatumVeljavnosti tipa DateTime. Želimo filtrirati vsa vina, katerih potekli certifikat je že pretekel (DatumVeljavnosti < DateTime.Now), jih izpisati na zaslon in zapisati v CSV datoteko poteceni_certifikati.csv. Nato ta vina odstranimo iz glavnega seznama vin.

var poteceni = seznamVin
    .Where(v => v.Certifikat != null && v.Certifikat.DatumVeljavnosti < DateTime.Now)
    .ToList();

// Izpis na zaslon:
Console.WriteLine("Potekeni certifikati:");
foreach (var v in poteceni)
{
    Console.WriteLine($"{v.Ime}, Certifikat veljaven do: {v.Certifikat.DatumVeljavnosti:dd.MM.yyyy}");
}

// Zapis v CSV (z glavo):
var poteceniCsv = new List<string>
{
    "Ime,DatumVeljavnosti"
};
poteceniCsv.AddRange(
    poteceni.Select(v => $"{v.Ime},{v.Certifikat.DatumVeljavnosti:yyyy-MM-dd}")
);
File.WriteAllLines("poteceni_certifikati.csv", poteceniCsv);

// Odstranitev poteklih iz glavnega seznama:
seznamVin = seznamVin.Except(poteceni).ToList();
        
Po tem ukazu bo datoteka poteceni_certifikati.csv vsebovala glavo in nato imena vin s pretečenimi certifikati, formatirano kot: Ime, YYYY-MM-DD. Hkrati pa so ta vina odstranjena iz seznamVin.

Pojdi nazaj na vrh

b) Urejanje seznama trgatev

Imamo naložen seznam List<Trgatev> seznamTrgatev. Želimo seznam urediti tako, da je najprej celotna razvrstitev po datumu trgatve naraščajoče, znotraj istega meseca pa elemente razvrstimo po količini pridelanega grozdja padajoče. Nato ta urejeni seznam izpišemo in ga zapišemo v CSV urejen_seznam_trgatev.csv.

// 1. Urejanje:
var urejeno = seznamTrgatev
    .OrderBy(t => new DateTime(t.DatumTrgatve.Year, t.DatumTrgatve.Month, 1))
    .ThenByDescending(t => t.KolicinaGrozdjaKg)
    .ToList();

// 2. Izpis na zaslon:
Console.WriteLine("Urejene trgatve:");
foreach (var t in urejeno)
{
    Console.WriteLine($"{t.DatumTrgatve:dd.MM.yyyy}, {t.SortaGrozdja}, {t.KolicinaGrozdjaKg} kg");
}

// 3. Zapis v CSV (z glavo):
var vrsticaTrgatev = new List<string>
{
    "DatumTrgatve,SortaGrozdja,KolicinaGrozdjaKg,SladkornaStopnja"
};
vrsticaTrgatev.AddRange(
    urejeno.Select(t =>
        $"{t.DatumTrgatve:yyyy-MM-dd},{t.SortaGrozdja},{t.KolicinaGrozdjaKg},{t.SladkornaStopnja}")
);
File.WriteAllLines("urejen_seznam_trgatev.csv", vrsticaTrgatev);
        
V CSV bo na voljo glava in nato vrstice v formatu: YYYY-MM-DD, Sorta, Kolicina, SladkornaStopnja, urejeno po mesecih (naraščajoče) in v istem mesecu po količini grozdja (padajoče).

Pojdi nazaj na vrh

c) Iskanje delavcev z največ opravljenimi trgatvami

Želimo ugotoviti, kateri delavci so sodelovali pri največ trgatvah. Uporabimo List<Trgatev> seznamTrgatev, kjer ima vsaka Trgatev lastnost List<Delavec> Udelezenci. Skupno število trgatev za posameznega delavca je število pojavitev v teh seznamih. Izpišemo top 5 delavcev na zaslon.

// 1. Združevanje in štetje:
var top5Delavcev = seznamTrgatev
    .SelectMany(t => t.Udelezenci)
    .GroupBy(d => d)
    .Select(grp => new { Delavec = grp.Key, SteviloTrgatev = grp.Count() })
    .OrderByDescending(x => x.SteviloTrgatev)
    .Take(5)
    .ToList();

// 2. Izpis:
Console.WriteLine("Top 5 delavcev z največ trgatvami:");
foreach (var x in top5Delavcev)
{
    Console.WriteLine($"{x.Delavec.Ime} {x.Delavec.Priimek}: {x.SteviloTrgatev} trgatev");
}
        
Rezultat bo izpis s petimi vrsticami: Ime Priimek: Število trgatev, urejeno od največ do najmanj. Če je manj kot 5 unikatnih delavcev, se izpišejo vsi.

Pojdi nazaj na vrh

d) Izpišite na zaslon povprečno količino pridelanega grozdja po mesecih

Želimo izračunati, kakšna je bila povprečna količina grozdja (v kg) za vsakega meseca (ne glede na leto ali pa upoštevamo leto). Vzemimo List<Trgatev> seznamTrgatev in razvrstimo po letu in mesecu, nato izračunamo povprečje.

// 1. Grupiranje po letu in mesecu:
var povprecjePoMesecih = seznamTrgatev
    .GroupBy(t => new { t.DatumTrgatve.Year, t.DatumTrgatve.Month })
    .Select(grp => new
    {
        Leto = grp.Key.Year,
        Mesec = grp.Key.Month,
        Povp = grp.Average(x => x.KolicinaGrozdjaKg)
    })
    .OrderBy(x => x.Leto).ThenBy(x => x.Mesec)
    .ToList();

// 2. Izpis:
Console.WriteLine("Povprečna količina grozdja po mesecih:");
foreach (var x in povprecjePoMesecih)
{
    Console.WriteLine($"{x.Leto}-{x.Mesec:00}: {x.Povp:F2} kg");
}
        
Izpis bo v obliki: YYYY-MM: Povprečje kg, urejeno kronološko po letu in mesecu.

Pojdi nazaj na vrh

e) Izpišite na zaslon najstarejše vino z veljavnim certifikatom

Želimo najti vino z veljavnim certifikatom (Certifikat.DatumVeljavnosti ≥ DateTime.Now), ki ima najstarejši letnik (LetoTrgatve najmanjša vrednost). Uporabimo List<Vino> seznamVin.

// 1. Filtriranje samo veljavnih in razvrstitev po letniku:
var najstarejsiVeljavni = seznamVin
    .Where(v => v.Certifikat != null && v.Certifikat.DatumVeljavnosti >= DateTime.Now)
    .OrderBy(v => v.LetoTrgatve)
    .FirstOrDefault();

// 2. Izpis:
if (najstarejsiVeljavni != null)
{
    Console.WriteLine($"Najstarejše vino z veljavnim certifikatom: " +
        $"{najstarejsiVeljavni.Ime} ({najstarejsiVeljavni.LetoTrgatve}), " +
        $"Certifikat veljaven do: {najstarejsiVeljavni.Certifikat.DatumVeljavnosti:dd.MM.yyyy}");
}
else
{
    Console.WriteLine("Ni najdenega vina z veljavnim certifikatom.");
}
        
Če ni nobeno vino s trenutno veljavnim certifikatom, se izpiše ustrezno sporočilo. Sicer pa se prikaže ime, letnik in datum poteka certifikata najstarejšega takega vina.

Pojdi nazaj na vrh

© Na voljo vsem razvijalcem – dokumentacija za kodo NPVaje.