Dizajniranje Softvera

Vežbe | 3. termin

Sadržaj

Pojmovi

Pojam Značenje
Delegat Tip koji opisuje potpis funkcije
Anonimna funkcija Funkcija koja nema ime
Lambda izraz Izraz kojim se kreira anonimna funkcija
Predikat Funkcija koja prima jedan parametar i vraća bool (TODO: Izbaciti ukoliko se prebacuje u naredni termin)
Događaj TODO

Delegati

Delegat je tip koji opisuje potpis funkcije.

Anonimne funkcije

Anonimna funkcija nema ime, kreira se samo navođenjem potpisa i tela sa implementacijom.

Naravno, da bi se takva funkcija koristila, moramo sačuvati referencu na nju, i pozivati je preko te reference.

Anonimna funkcija se može kreirati na više načina. U nastavku ćemo proći kroz dva: Delegat sintaksa i Lambda izraz.

Delegat sintaksa

delegate (parametri) { naredbe }

Parametri moraju imati naveden tip.

Primer

Delegat (tip funkcije) BinaryOp je definisan kao:

delegate int BinaryOp(int a, int b);

Anonimnu funkciju ćemo kreirati na sledeći način:

BinaryOp add = delegate (int x, int y) {
    return x + y;
};

Lambda izrazi

Izraz koji kreira novu anonimnu funkciju i vraća referencu na nju.

(parametri) => izraz
(parametri) => { naredbe }

Parametri ne moraju imati naveden tip.

Primer

Koristićemo delegat BinaryOp iz primera delegat sintakse.

Anonimnu funkciju ćemo kreirati na sledeći način

BinaryOp add = (x, y) => x + y;

Umesto izraza možemo napisati blok sa naredbama:

BinaryOp add = (x, y) => {
    return x + y;
};

Korišćenje

U prethodnim primerima smo kreirali anonimnu funkciju koja sabira dva broja. Referencu na tu funkciju smo čuvali u promenljivoj add.

Sada, funkciju možemo pozivati preko te promenljive:

int result = add(17, 33);

Događaji

Događaj, tj. event, je specijalni član klase ili objekta, koji se ponaša kao referenca na funkciju određenog tipa (delegata), a u pozadini zapravo čuva nekakvu listu referenci na funkcije tog tipa, i na poziv event-a (kao što bi se pozvala obična funkcija) pozivaju se redom sve funkcije čije su reference tu sačuvane.

Pre nego što se referenca na naku funkciju doda u event, odnosno pre nego što se ona prijavi/pretplati (subscribe) na event, event će biti null.

Prvo ćemo definisati tip event-a, odnosno tip funkcija koje će event čuvati:

public delegate void TempChangeHandler(double temp);

Zatim, kreiraćemo klasu čija će instanca predstavljati termometar koji simulira merenje temperature:

class Termometer
{
    double current = 20;

    public event TempChangeHandler TempChange;

    public void Start()
    {
        Random rnd = new Random();

        while (true)
        {
            Thread.Sleep(1000);               // Čekanje, 1000 milisekundi
            int change = rnd.Next(3) - 1;     // Moguće vrednosti: -1, 0, 1
            current += change;                // Temperatura će se smanjiti, povećati ili ostati ista

            if (change != 0)                  // Ukoliko je došlo do promene ...
                TempChange?.Invoke(current);  // ... i ukoliko TempChange nije null, pozivaju se sve prijavljene funkcije
        }
    }
}

U klasi Termometer smo definisali event koji se zove TempChange, a koji je tipa TempChangeHandler. U metodi Start se nalazi logika koja simulira merenje, i tu je, realizovano i pozivanje, tj. aktiviranje tog event-a.

Kada nema prijavljenih metoda event TempChange će biti null. Ukoliko tada aktiviramo event dobićemo grešku NullReferenceException. Zato moramo proveriti da li je on null, i aktivirati ga samo ukoliko nije. Možemo koristiti ?. operator, koji evaluira ceo poziv u null ukoliko je TempChange null i tada ne radi poziv metode Invoke, a u suprotnom regularno poziva Invoke. Drugi način za ovu proveru bi bio:

if (change != 0 && TempChange != null)
    TempChange(current); // ili TempChange.Invoke(current)

Takođe, kreiraćemo klasu sa metodom koja ispisuje temperaturu:

class TempLogger
{
    public void Log(double temp)
    {
        Console.WriteLine("[Logger] " + temp);
    }
}

Sada ćemo prijaviti više različitih tipova funkcija na event TempChange:

static class Program
{
    static void LogTemp(double temp)
    {
        Console.WriteLine("[Static] " + temp);
    }

    public static void Main(string[] args)
    {
        var sensor = new Termometer();
        var logger = new TempLogger();

        sensor.TempChange += LogTemp;                                  // Statička metoda (nema "this")
        sensor.TempChange += logger.Log;                               // Metoda uvezana sa objektom (ima "this")
        sensor.TempChange += t => Console.WriteLine("[Anonim] " + t);  // Anonimna funkcija

        sensor.Start();
    }
}

Prijavljivanje funkcije na event se vrši pomoću operatora +=. Odjavljivanje se analogno tome vrši preko operatora -=, samo kao desni operand navedemo referencu na funkciju koju smo prethodno prijavili, i onda će biti odjavljena.

Svaki put kada se temperatura promeni u konzoli se ispisuje:

[Static] broj
[Logger] broj
[Anonim] broj

«< 2. Termin 4. Termin »>