Õppisin uue andmetüübi – dynamics

 

C# uues versioonis 4.0, õigemini .NET raamistiku uues versioonis tekkis uus, alguses mänguasjake, aga tegelikult päris olulise sisuga uuendus – dynamic. Paistab pisike uuendus, aga muudab põhimõtteliselt senist CLR (common language runtime) ja CTS (common type system) maailma.

Üheks oluliseks nurgakiviks senises maailmas oli tugev tüübikindlus. Programmmi transleerimsie ajal on igal asjal (muutujal) oma kindel tüüp. Ja kuna on kindel tüüp, siis me teame, mida on sellega võimalik peale hakata (kindel hulk meetodeid) ja kuidas seda midagi peale hakata (need meetodid ise on ka kindlad). See võimaldab translaatoril kõik ilusti üle kontrollida ja vältida minu tehtud vigade sattumist töötavasse programmi. lisaks see võimaldab translaatoril minu mõtte teisendada just õigeteks meetoditeks – funktsioonide väljakutseteks. TUlemuseks on palju korrektsem ja kompaktsem ja “lolllikindlam” programm.

sellise maailma nimi on staatiline tüübikindlus – vanal heal ajal nimetati seda ka varaseks sidumiseks (early binding). Selles mõttes on CLR ka SLR (static language runtime) ja CTS ka STS (static type system).

Elu on aga keerulisem – staatilise maailma kõrval elab oma elu dynaamiline, paindlik, lõbus ja kiire maailm. Kõikvõimalikud dynaamilised keeled ja muu taoline, kus põhirõhk on pandud lihtsusele, kiirele lahendusele, paindlikkusele. Selles maailmas pannakse sageli programmi kood kokku töö käigus ja lihtsatest vahenditest. Ja selle maailmaga on vaja asju ajada. Ning sellepärast lisatigi uus võimalus, tüüp ja terve hulk põnevaid klasse raamistikku ja keeltesse – staatilistesse keeltesse, et need saaks dünaamilistega suhelda nende omas keeles.

Alustame algusest:

dynamic d;
d = 0;
d = "Henn";

nagu näed, saab muutujale “d” omistada nii arvukesi kui tekste. seda sai ka objektiga?

object o;
o = 0;
o = “Henn”;

proovi nüüd aga sellist vigurit

d++;    // toimib ilusti, translaator ei pahanda ja programm teeb nii nagu tahtsid
o++;    // translaator annab vea – objektiga ei oska keegi ++ teha.

d.ToUpper();   // toimib ilusti
o.ToUpper();   // translaator annab vea

samas järgmine koodijupp toimib ilusti, aga kui proovida lisada lõppu veelkord rida (2) – saad vea – ja mitte transleerimisel vaid programmi jooksutamisel.

d = 0;                                               // (1)
Console.WriteLine(++d);                    // (2)
d = “Henn”;                                      // (3)
Console.WriteLine(d.ToUpper());        // (4)

see ongi staatilise sidumise (static binding, neiupõlvenimeg aearly binding) ja dynaamilise sidumise (ehk late binding) erinevus. Translaatoril pole õrna aimugi, kas nimetet objektiga saab ühte või teist operatsiooni teha – seda peab vaatama töö käigus. Saams on muutujatel ikkagi oma tüübid ja tüübikindlus on ka ilusasti paigal – see lihstalt muutub töö käigus dünaamiliselt. Proovi näiteks:

dynamic d;
d=0;
Console.WriteLine(“muutuja tüüp on {0} ja väärtus {1}”, d.GetType().Name, d);
d=”Henn”;
Console.WriteLine(“muutuja tüüp on {0} ja väärtus {1}”, d.GetType().Name, d);

Nüüd teeme midagi palju lõbusamat –  lisasin oma koodi ühe väikese klassi:

class PropBag : DynamicObject
{
private Dictionary<string, dynamic> prop = new Dictionary<string, dynamic>();
public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        prop[binder.Name] = value;
        return true;
    }
public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = prop[binder.Name];
        return true;
    }
}

sellist klassi saad väga vahvalt kasutada:

dynamic pg = new PropBag();
pg.Nimi = "Henn";
pg.Eriala = "õpetaja";
pg.Vanus = 54;
Console.WriteLine("on {0} vanusega {1}", pg.Nimi, pg.Vanus);
Console.WriteLine("järgmisel aastal on {0} vanusega {1}", pg.Nimi.ToUpper(), ++(pg.Vanus));

Tähelepanuks – kuna translaatoril ega ka IDE-l ei ole õrna aimugi, mida meie muutujaga teha saab, mis atribuudid (propertid) tal on, siis intellisensist pole selle koha peal koodis miskit kasu. Ka ei suuda translaator avastada, kui sa küsid pg.vanus // väikse tähega, see annab vea alles töö käigus. Ning see ongi lõiv, mis me maksame paindlikkuse eest.

Teeme selle klassi veel vahvamaks. Lisame klassile ühe meetodi veel:

public override IEnumerable<string> GetDynamicMemberNames()
{
    return from x in prop select x.Key;
}

nüüd võib programmis hakata uurima, mis propertid meie tegelasel on, seda näiteks koodijupiga:

foreach (var x in pg.GetDynamicMemberNames()) Console.WriteLine(“Meie tegelasel on property {0}”, x));

aga mida ma ei oska teha, see on sellist propertit välja lugeda. Asja teeb aga võimalikkuks veel üks kaval meetodipaar (kui ei taha, pole SET meetodit vaja)

public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
    prop[(string)(indexes[0])] = value;
    return true;
}

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
    result = prop[(string)(indexes[0])];
    return true;
}

See lisab meie lõbusale propertypagile indexeri – laisk nagu ma olen, ei viitsi ma analüüsida ega pahandada, kui keegi pöördub rohkem kui ühe indeksiga minu poole, ma ignoreerin ülejäänusid (seepärast seal see (string)(index[0]))

Nüüd saame me kasutada oma klassi poole pöördumiseks sellist koodijuppi:

foreach (var x in pg.GetDynamicMemberNames())
Console.WriteLine(“Meie tegelasel on property {0} selle tüüp on {1} ja väärtus {2}”, x, pg[x].GetType().Name, pg[x]));

Vot sellise loo kirjutasin täna uuest tüübist, mis ma ära õppisin.

Kogu tänane programmikood (igasuguse muu lisandusega) on loo lõpus

selleks korraks kõik

Henn ehk Sarviktaat

——————————————————————————————————————

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;

namespace dynamics
{
    class Karu
    {
        public int Nina() { return 7; }
    }

    class Hunt
    {
        public string Nina() { return "sinine"; }
    }

    class Inimene : DynamicObject
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            dynamic d;
            d = 0;
            d++;
            Tryki(d);
            Console.WriteLine(KasInteger(d));

            d = "Henn";
            Tryki(d);
            Console.WriteLine(KasInteger(d));

            dynamic pg = new PropBag();
            pg.Nimi = "Henn";
            pg.Eriala = "õpetaja";
            pg.Vanus = 54;

            pg["Eesliide"] = "Sir.";
            Console.WriteLine("on {0} vanusega {1}", pg.Nimi, pg.Vanus);
            Console.WriteLine("järgmisel aastal on {0} vanusega {1}", pg.Nimi.ToUpper(), ++(pg.Vanus));
            Console.WriteLine("on {0} vanusega {1}", pg.Nimi, pg.Vanus);

            foreach (var x in pg.GetDynamicMemberNames()) Tryki(pg[x], x);

            dynamic y;
            y = new Karu(); Tryki(y.Nina());
            y = new Hunt(); Tryki(y.Nina());

        }

        static bool KasInteger(dynamic x)
        {
            return x is int;
        }

        static void Tryki(dynamic x)
        {
            Console.WriteLine("Tegelane {0} väärtusega {1}", x.GetType().Name, x);
        }
        static void Tryki(dynamic x, string nimi)
        {
            Console.WriteLine("{2} on tegelane {0} väärtusega {1}", x.GetType().Name, x, nimi);
        }

    }

    class PropBag : DynamicObject
    {
        private Dictionary<string, dynamic> prop = new Dictionary<string, dynamic>();

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            prop[binder.Name] = value;
            return true;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = prop[binder.Name];
            return true;
        }

        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
        {
            prop[(string)(indexes[0])] = value;
            return true;
        }

        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
        {
            result = prop[(string)(indexes[0])];
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return from x in prop select x.Key;
        }
    }
}

This entry was posted in Dynamic. Bookmark the permalink.

Lisa kommentaar

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Muuda )

Twitter picture

You are commenting using your Twitter account. Log Out / Muuda )

Facebook photo

You are commenting using your Facebook account. Log Out / Muuda )

Google+ photo

You are commenting using your Google+ account. Log Out / Muuda )

Connecting to %s