Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Ars programatica

Udělat dobrý sotware je řemeslo, udělat výjimečný software je umění
Inicializéry (C# 3.0)

Jednou z novinek v jazyce C# 3.0 jsou inicializéry (anglicky initializers), typický zástupce rodu syntaktický cukr. Jedná o speciální konstrukci, která volitelně následuje po volání konstruktoru a ve které je umožněn implicitní přistup k nově vytvořené instanci a jejím datovým (pole, vlastnosti) členům. Zřejmější to bude na příkladu:

Mějme třídu Alfa:

public class Alfa
{
public int i;
public string s;
}

Její instanci je s pomocí inicializéru možno vytvořit takto:

Alfa a = new Alfa { i = 1, s = "a" };

což je zcela ekvivalentní zápisu: 

Alfa newAlfa = new Alfa();
newAlfa.i
= 1;
newAlfa.s
= "a";
Alfa a
= newAlfa;

(Poznámka: Závorky ve volání konstroktoru nejsou při použití inicializátoru povinné, takže Alfa a = new Alfa { i = 1, s = "a" }; a Alfa a = new Alfa () { i = 1, s = "a" }; jsou zcela rovnocenné zápisy.)

Z tohoto pohledu se tedy zdá, že inicializér nic nového nepřináší. Využití inicializéru je především v souvislosti s anonymní typy a LINQ. V následujícím příkladě (alfas je kolekce instancí třídy Alfa): 

var small = from a in alfas select new {Number = a.i};

představuje výraz new {Number = a.i} vytvoření instance anonymního typu. Kompilátor z inicializéru vyčte strukturu anonymního typu a především jména vlastností, která by ze standarní syntaxe konstruktoru nebylo vyčíst nedala.

Inicializace kolekcí

Větší použití zřejmě nalezne inicializér při vytváření kolekcí. Výše zmiňovanou kolekci alfas je s pomocí inicializéru možno vytvořit následovně:

 

List<Alfa> alfas = new List<Alfa>
{
new Alfa {i=1, s="one"},
new Alfa {i=2, s="two"},
new Alfa {i=3, s="three"},
};

což je ekvivalentní zápisu:

List<Alfa> alfas = new List<Alfa>();
Alfa a;
a
= new Alfa();
a.i
= 1;
a.s
= "one";
alfas.Add(a);
a
= new Alfa();
a.i
= 2;
a.s
= "two";
alfas.Add(a);
a
= new Alfa();
a.i
= 3;
a.s
= "three";
alfas.Add(a);

Který z obou zápisů je čitelnější je nabíledni.

Inicializace členských proměnných

Inicializéry umožňují též komplexní inicializaci členských proměnných třídy v deklaraci, což bylo zatím nutno řešit v explicitně konstruktoru. Opět ukáži na příkladu, protože tak to bude nejsrozumitelnější:

public class UsingInitializers
{
private List<Alfa> alfaList = new List<Alfa>
{
new Alfa {i=1, s="one"},
new Alfa {i=2, s="two"},
new Alfa {i=3, s="three"},
};
};

Vnořené inicializéry

Mějme kromě výše zmíněné třídy Alfa ještě třídu Omega:

public class Omega
{
public Alfa alfa;
}

Její instanci je s pomocí inicializéru možno vytvořit takto:

Omega w = new Omega { alfa = new Alfa { i = 3, s = "w" } };

Pozor, vnořený inicializér nevytváří instanci, kterou inicializuje. Proto je ve výše uvedeném příkladě třeba použít new Alfa(). Pokud by ovšem třída Omega byla definována následovně:

public class Omega2
{
public Alfa alfa = new Alfa();
}

Potom lze napsat jen:

Omega2 w2 = new Omega2 { alfa = { i = 3, s = "w" } };

Podobný zápis, ale s třídou Omega, tedy:

Omega w = new Omega { alfa = { i = 3, s = "w" } };

způsobí run-time exception.

 

(Pozn. Opravil jsem některé chyby, na které jsem byl upozorněn v komentářích).

Posted: Tuesday, August 05, 2008 4:18 PM by pbouda
Vedeno pod: , ,

Komentář

Tom napsal:

Správnější překlad je spíš inicializér, iniciátor spíš zní jako někdo, kdo začíná konverzaci...

# August 5, 2008 6:00 PM

jarousek napsal:

v posledním kusu kódu Ti zřejmě přbývá "new Alfa", nebo se mýlím?

# August 5, 2008 6:31 PM

Pepa napsal:

Alfa a = new Alfa { i = 1, s = "a" };

NENÍ zcela ekvivalentní zápisu:

Alfa a = new Alfa();

a.i = 1;

a.s = "a";

rozdíl je docela zásadní a to hlavně z hlediska vícevláknového přístupu

- to první je Thread Safe

- to druhé nikoliv

Doporučuji podívat se na to co vytvoří kompilátor.

# August 5, 2008 8:14 PM

pbouda napsal:

Tom: přemýšlel jsem nad tím, ale "inicializér" mi znělo jako přílišný anglicismus. Ale ještě se nad tím zamyslím a možná to přepíši.

jarousek: Právě že nepřebývá, v tom je ten fór. Omega a Omega2 se liší v tom, že Omega2 inicializuje svoji alfu již v deklaraci, takže je definovaná, zatímco v Omeze má defaultní hodnotu, což je null. Proto se v iniciátoru Omegy musí uvést new Alfa(), zatímco v iniciátoru Omegy2 nikoliv.

# August 5, 2008 8:16 PM

pbouda napsal:

Pepa: díval jsem již když jsem to psal, a nevidím nic, co by z prvního zápisu dělalo thread-safe. Ani specifikace o ničem takovém nemluví. Ale zítra to ještě zkontroluji.

# August 5, 2008 8:24 PM

Bobris napsal:

Pepa ma pravdu ze to neni zcela ekvivalentni tomu zapisu, ale v tomto jednoduchem pripade se rozdil nikdy neprojevi.

Ale v tomto prikladu to jiz videt je:

   public class Zeta

   {

       public static Alfa alfa;

       public void init()

       {

           alfa = new Alfa() { i = 42 };

           // vs

           alfa = new Alfa();

           alfa.i = 13;

       }

   }

Prvni radek zajisti ze nikdo neuvidi static promennou a nastavenym objektem Alfa kde by se i==0. Coz u dalsich dvou radky neplati. Tim asi Pepa myslel ze to je threadsafe.

a i jarousek ma pravdu protoze tak jak to je napsany, tak:

Omega w = new Omega { alfa = new Alfa { i = 3, s = "w" } };

vyjimku nevyvola, vyvolal by ji radek:

Omega w = new Omega { alfa = { i = 3, s = "w" } };

# August 5, 2008 9:53 PM

Bobris napsal:

Jeste jsem zapomel ze toto:

"představuje výraz new {Number = a.i} vytvoření instance anonymního typu. Protože pro anonymní typy nejsou generovány parametrické konstruktory, není možno instanci anonymního typu předat iniciační hodnoty v konstruktoru a je nutno iniciovat již vytvořenou instanci. To by však ve výše uvedeném příkladě nebylo možné, protože instance anonymního typu není programátorovi nikde k dispozici. Právě zde se uplatní iniciátor, který instanci programátorovi implicitně zpřístupní a umožní tak její inicializaci."

Neni pravda. Anonymni typy prave maji vsechny fieldy readonly a property pouze jako getry. Inicializace se tedy provadi vyhradne konstruktorem a tak jedine co to sdili s iniciatory je podobna syntaxe, v pozadi je neco jineho. Ono to je temer jedno, ale Anonymni typy tim ze nejsou mutabilni (to je slovo :-)), tak propaguji/podporuji funkcionalni programovani.

# August 5, 2008 10:03 PM

pbouda napsal:

Bobris, Pepa: Takže je to tak, jak říkáte vy.

Alfa a = new Alfa { i = 1, s = "a" };

je ekvivalentní s

Alfa <>g__initLocal0 = new Alfa();

<>g__initLocal0.i = 1;

<>g__initLocal0.s = "a";

Alfa a = <>g__initLocal0;

což opravdu není totéž, jako

Alfa a = new Alfa();

a.i = 1;

a.s = "a";

(je tam navíc ta temporary proměnná).

Napsal jsem, co jsem napsal, a myslel, co jste napsali vy. Inu, práce kvapná, málo platná. :-/

S tím anonymním typem máte, Bobrisi, také pravdu, opravím to.

Díky za upozornění.

# August 6, 2008 8:59 AM
Nejsou povoleny nové komentáře k tomuto příspěvku