Klassid

C# keeles on klass (class) kasutaja poolt defineeritud viitetüüp (reference type), mis on objektorienteeritud programmeerimise alustala ning toimib objektide loomise kavandina või mustrina. Klassid koondavad andmeid ja käitumist ühtseks loogiliseks üksuseks.

Siin on põhjalik ülevaade klassidest tuginedes allikatele:

1. Klassi olemus ja mälu

  • Viitetüüp: Klassid on viitetüübid, mis tähendab, et kui loote objekti, eraldatakse sellele mälu kuhjas (managed heap). Muutuja ise hoiab vaid viidet (mäluaadressi) sellele objektile.
  • Blueprint: Klassi deklaratsioon on kui joonis, mille põhjal luuakse tööajal isendid ehk objektid. Ühest klassist saab luua piiramatult objekte, millest igaühel on oma andmed.
  • Ühtne tüübisüsteem: Kõik C# klassid pärinevad otseselt või kaudselt ühisest baasklassist System.Object.

2. Klassi liikmed

Klass koosneb erinevatest liikmetest, mis määravad selle andmed ja funktsionaalsuse:

  • Väljad (fields): Klassi sisesed muutujad andmete hoidmiseks.
  • Omadused (properties): Võimaldavad kontrollitud ligipääsu andmetele get ja set aktsessorite kaudu. C# 14 tutvustas märksõna field, mis võimaldab kirjutada omadusi ilma käsitsi taustavälja deklareerimata.
  • Meetodid: Koodiplokid, mis sooritavad tegevusi.
  • Konstruktorid: Erimetodid, mida kutsutakse välja objekti loomisel (new märksõnaga), et algväärtustada selle liikmed.
  • Sündmused (events): Võimaldavad klassil teavitada teisi objekte teatud tegevuste toimumisest.

3. Pääsumodifikaatorid

Klassi liikmete nähtavust kontrollitakse modifikaatoritega:

  • public: Juurdepääs on lubatud igalt poolt.
  • private: Juurdepääs on lubatud ainult sama klassi seest (vaikimisi tase liikmetele).
  • protected: Juurdepääs on lubatud samas klassis ja sellest tuletatud klassides.
  • internal: Juurdepääs on lubatud ainult sama projekti (kooste) piires.

4. Eri tüüpi klassid ja modifikaatorid

  • static: Staatilisest klassist ei saa luua eksemplare; kõik selle liikmed peavad olema staatilised ja need kuuluvad klassile endale, mitte objektidele.
  • abstract: Abstraktne klass on mõeldud ainult baasklassiks teistele klassidele ning sellest ei saa luua otseseid isendeid.
  • sealed: Suletud klass, millest ei saa edasi pärida.
  • partial: Võimaldab ühe klassi definitsiooni jagada mitme eraldi faili vahel, mis on kasulik automaatselt genereeritud koodi puhul.

5. Klassid ja OOP põhimõtted

Klassid on vahendiks nelja peamise objektorienteeritud põhimõtte rakendamiseks:

  • Pärilus (Inheritance): Võimalus luua uusi klasse olemasolevate põhjal, kasutades süntaksit class SubClass : BaseClass.
  • Polümorfism: Võimalus üle kirjutada baasklassi meetodeid (virtual ja override), et muuta käitumist tuletatud klassides.
  • Kapseldus: Sisemiste detailide peitmine ja ainult vajaliku liidese paljastamine.
  • Abstraktsioon: Keeruliste süsteemide mudeldamine asjakohaste klasside ja atribuutide abil.

Uuendusena pakub C# 14 ka laiendusliikmeid (extension members), mis võimaldavad lisada olemasolevatele klassidele uusi meetodeid ja omadusi ilma neid muutmata või neist pärimata.

1. Klassi põhistruktuur

public class Isik
{
    // VÄLJAD (fields) – privaatsed andmed
    private string _nimi;
    private int _vanus;

    // OMADUSED (properties) – kontrollitud ligipääs
    public string Nimi
    {
        get { return _nimi; }
        set { _nimi = value; }
    }

    // Lühike omadus (auto-property)
    public int Vanus { get; set; }

    // Kirjutuskaitstud omadus
    public bool OnTäisealine => _vanus >= 18;

    // KONSTRUKTOR – objekti loomisel kutsutakse välja
    public Isik(string nimi, int vanus)
    {
        _nimi = nimi;
        _vanus = vanus;
    }

    // MEETOD
    public void Tervita()
    {
        Console.WriteLine($"Tere! Olen {_nimi}, {_vanus} aastane.");
    }
}

// Objekti loomine
Isik isik1 = new Isik("Mari", 25);
Isik isik2 = new Isik("Jaan", 17);

isik1.Tervita();                            // Tere! Olen Mari, 25 aastane.
Console.WriteLine(isik2.OnTäisealine);      // False

2. Konstruktorid

public class Auto
{
    public string Mark { get; set; }
    public string Mudel { get; set; }
    public int Aasta { get; set; }

    // Vaikekonstruktor
    public Auto()
    {
        Mark = "Tundmatu";
        Mudel = "Tundmatu";
        Aasta = 2000;
    }

    // Parameetritega konstruktor
    public Auto(string mark, string mudel, int aasta)
    {
        Mark = mark;
        Mudel = mudel;
        Aasta = aasta;
    }

    // Konstruktor kutsub teist konstruktorit
    public Auto(string mark, string mudel) : this(mark, mudel, 2024)
    {
    }

    public override string ToString()
        => $"{Mark} {Mudel} ({Aasta})";
}

// Erinevad loomisviisid
Auto a1 = new Auto();
Auto a2 = new Auto("Toyota", "Corolla", 2020);
Auto a3 = new Auto("BMW", "X5");

Console.WriteLine(a1);     // Tundmatu Tundmatu (2000)
Console.WriteLine(a2);     // Toyota Corolla (2020)
Console.WriteLine(a3);     // BMW X5 (2024)

3. Pääsumodifikaatorid

public class Pangakonto
{
    // private – ainult klassi sees
    private double _saldo;
    private string _pin;

    // public – kõigile nähtav
    public string Omanik { get; }

    // protected – ka alamklassides nähtav
    protected string Kontonumber { get; }

    // internal – ainult projekti sees
    internal DateTime LoodudKuupäev { get; }

    public Pangakonto(string omanik, string pin, double algsaldo)
    {
        Omanik = omanik;
        _pin = pin;
        _saldo = algsaldo;
        LoodudKuupäev = DateTime.Now;
    }

    // Avalik meetod – kasutab privaatset välja
    public bool KontrolliPin(string pin) => _pin == pin;

    public void Tõsta(double summa, string pin)
    {
        if (!KontrolliPin(pin))
        {
            Console.WriteLine("Vale PIN!");
            return;
        }
        if (summa > _saldo)
        {
            Console.WriteLine("Ebapiisav saldo!");
            return;
        }
        _saldo -= summa;
        Console.WriteLine($"Väljastatud {summa}€. Saldo: {_saldo}€");
    }

    public double VaataSaldo(string pin)
    {
        if (!KontrolliPin(pin)) return -1;
        return _saldo;
    }
}

var konto = new Pangakonto("Mari", "1234", 1000);
konto.Tõsta(200, "1234");           // Väljastatud 200€. Saldo: 800€
konto.Tõsta(500, "0000");           // Vale PIN!

4. Eri tüüpi klassid

Static klass

// Staatilisest klassist ei saa objekte luua
public static class Matemaatika
{
    public static double PI = 3.14159;

    public static double Ruut(double x) => x * x;
    public static double Ringjoon(double r) => 2 * PI * r;
    public static bool OnAlgarv(int n)
    {
        if (n < 2) return false;
        for (int i = 2; i <= Math.Sqrt(n); i++)
            if (n % i == 0) return false;
        return true;
    }
}

// Kasutamine ilma objektita
Console.WriteLine(Matemaatika.Ruut(5));         // 25
Console.WriteLine(Matemaatika.OnAlgarv(17));    // True

Abstract klass

// Abstraktsest klassist ei saa otse objekte luua
public abstract class Loom
{
    public string Nimi { get; set; }

    public Loom(string nimi) => Nimi = nimi;

    // Abstraktne meetod – PEAB olema alamklassis
    public abstract string TeeSHääl();

    // Tavaline meetod – päritakse
    public void Tutvusta()
        => Console.WriteLine($"Olen {Nimi} ja ütlen: {TeeSHääl()}");
}

public class Koer : Loom
{
    public Koer(string nimi) : base(nimi) { }
    public override string TeeSHääl() => "Auh!";
}

public class Kass : Loom
{
    public Kass(string nimi) : base(nimi) { }
    public override string TeeSHääl() => "Mjäu!";
}

Loom[] loomad = { new Koer("Rex"), new Kass("Miki") };
foreach (var loom in loomad)
    loom.Tutvusta();
// Olen Rex ja ütlen: Auh!
// Olen Miki ja ütlen: Mjäu!

Sealed klass

// Sealed klassist ei saa pärida
public sealed class Konfiguratsioon
{
    public string ServerAadress { get; } = "localhost";
    public int Port { get; } = 8080;
}

// Järgmine kood annaks VIGA:
// public class MuuKonfig : Konfiguratsioon { }

5. OOP Põhimõtted

Pärilus (Inheritance)

public class Sõiduk
{
    public string Mark { get; set; }
    public int Kiirus { get; protected set; }

    public Sõiduk(string mark) => Mark = mark;

    public virtual void Kiirendam(int summa)
    {
        Kiirus += summa;
        Console.WriteLine($"{Mark} kiirus: {Kiirus} km/h");
    }
}

public class Elektriauto : Sõiduk
{
    public int Aku { get; private set; } = 100;

    public Elektriauto(string mark) : base(mark) { }

    // override – kirjutab üle baasklassi meetodi
    public override void Kiirendam(int summa)
    {
        Aku -= summa / 10;
        base.Kiirendam(summa);      // kutsub baasklassi meetodit
        Console.WriteLine($"Aku: {Aku}%");
    }
}

var tesla = new Elektriauto("Tesla");
tesla.Kiirendam(50);
// Tesla kiirus: 50 km/h
// Aku: 95%

Polümorfism

public class Kujund
{
    public virtual double Pindala() => 0;
    public void Info() => Console.WriteLine($"Pindala: {Pindala():F2}");
}

public class Ring : Kujund
{
    private double _raadius;
    public Ring(double r) => _raadius = r;
    public override double Pindala() => Math.PI * _raadius * _raadius;
}

public class Ristkülik : Kujund
{
    private double _laius, _kõrgus;
    public Ristkülik(double l, double k) { _laius = l; _kõrgus = k; }
    public override double Pindala() => _laius * _kõrgus;
}

// Polümorfism – sama meetod, erinev käitumine
List<Kujund> kujundid = new()
{
    new Ring(5),
    new Ristkülik(4, 6),
    new Ring(3)
};

foreach (var k in kujundid)
    k.Info();
// Pindala: 78.54
// Pindala: 24.00
// Pindala: 28.27

Täisprogramm – kõik koos

using System;
using System.Collections.Generic;

// Abstraktne baasklass
public abstract class Töötaja
{
    public string Nimi { get; }
    public int Id { get; }
    private static int _järjekord = 1;

    protected Töötaja(string nimi)
    {
        Nimi = nimi;
        Id = _järjekord++;
    }

    public abstract double ArvutaPalk();

    public virtual void Info()
        => Console.WriteLine($"[{Id}] {Nimi} – Palk: {ArvutaPalk():F2}€");
}

// Alamklass – täistööajaga töötaja
public class TäisAeg : Töötaja
{
    public double KuuPalk { get; }

    public TäisAeg(string nimi, double kuuPalk) : base(nimi)
        => KuuPalk = kuuPalk;

    public override double ArvutaPalk() => KuuPalk;
}

// Alamklass – osalise tööajaga töötaja
public class OsalineAeg : Töötaja
{
    public double Tunnipalk { get; }
    public int Tunnid { get; }

    public OsalineAeg(string nimi, double tunnipalk, int tunnid) : base(nimi)
    {
        Tunnipalk = tunnipalk;
        Tunnid = tunnid;
    }

    public override double ArvutaPalk() => Tunnipalk * Tunnid;

    public override void Info()
    {
        base.Info();
        Console.WriteLine($"   → {Tunnid}h × {Tunnipalk}€");
    }
}

// Staatiline abiklass
public static class PalgaArvutus
{
    public static double KoguPalk(List<Töötaja> töötajad)
    {
        double kokku = 0;
        foreach (var t in töötajad) kokku += t.ArvutaPalk();
        return kokku;
    }
}

class Program
{
    static void Main()
    {
        var tiim = new List<Töötaja>
        {
            new TäisAeg("Mari", 2500),
            new TäisAeg("Jaan", 3200),
            new OsalineAeg("Peeter", 12.5, 80),
            new OsalineAeg("Anna", 15.0, 60)
        };

        Console.WriteLine("Töötajate nimekiri:");
        foreach (var t in tiim) t.Info();

        Console.WriteLine($"\n Kokku palka: {PalgaArvutus.KoguPalk(tiim):F2}€");
    }
}
```

**Väljund:**
```
Töötajate nimekiri:
[1] Mari – Palk: 2500.00€
[2] Jaan – Palk: 3200.00€
[3] Peeter – Palk: 1000.00€
   → 80h × 12.5€
[4] Anna – Palk: 900.00€
   → 60h × 15€
Kokku palka: 7600.00€