\


 Monday, 31 May 2004
"Některé věci mají k sobě nepopsatelně blízko" - příkaz switch v jazyce C# a metoda String.IsInterned

Jazyk C# povoluje konstrukci switch, v níž se podmíněná sekce kódu zvolí podle shody proměnné typu string s konstantním řetězcem v sekci case.

 

switch (myVariable1 + myVariable2)

{

            case “sekce1”:

            {

                        //Zpracuj

break;

 }

case “sekce 2”:

           {

                        //Zpracuj

break;

}

default:

{

            //Nedelej nic

            break;

}

 

}

 

I když napsání swich výrazu s řetězcem je pro vývojáře jednoduchou záležitostí, zpracování switch výrazu kompilátorem již tak triviální není. Představte si, že porovnání shody řetězců by bylo prováděno znak po znaku. S prodloužením řetězců by docházelo k lineárnímu nárůstu doby potřebné pro porovnání řetězců. Jak asi ze své zkušenosti víte, příkaz case je vždy stejně rychlý bez ohledu na délku řetězců a k žádnému neohrabanému porovnávání řetězců znak po znaku v žádném případě nedochází. Jak je tedy příkaz case kompilátorem zpracován a rozvinut?

 

Všechny literály (string someString = “Ja jsem literal“) jsou při JIT kompilaci aplikace přidány do speciální hashovací tabulky vytvářené  a spravované běhovým prostředím, v níž klíčem je řetězec a hodnotou odkaz (reference, adresa v paměti) na tento řetězec. Podstatné je, že řetězec se stejnou sekvencí znaků se v tabulce vyskytuje nanejvýš jednou. Takže pokud nadeklaruji  kromě výše uvedené proměnné someString proměnnou  someString2  a inicializuji ji stejným řetězcem jako proměnnou someString ( string someString2 = “Ja jsem literal“)),  tak běhové prostředí při JIT kompilaci zjistí, že v hashovací tabulce klíč Ja jsem literal již existuje a proto do proměnné someString2 přiřadí odkaz na objekt umístěný pod tímto klíčem. Proměnné someString a someString2 odkazují poté na stejné místo v paměti, což si lze snadno ověřit voláním metody ReferenceEquals.

Z umístění do hashovací tabulky jsou při JIT kompilaci vyloučeny řetězce, jež jsou  konstruovány dynamicky za běhu programu. Takže řetězec v proměnné string someString3 = “Ja jsem literal“ + someString2 přidán nebude. Třída String má ale statickou metodu Intern, jejím účelem je dodatečné vložení řetězce a odkazu na něj do hashovací tabulky. Příkazem String.Intern(someString3) je do hashovací tabulky přidán obsah proměnné someString3.

Když  nechceme řetězec do hashovací tabulky přidat, ale chceme se jen dotázat, zda je v hashovací tabulce již obsažen, použijeme další statickou metody String.IsInterned. Metoda IsInterned při nalezení řetězce v hashovací tabulce vrací odkaz na tento řetězec. Když řetězec v hashovací tabulce není, metoda IsInterned vrátí null.

Nyní si už můžete sami odvodit, jak kompilátor efektivně zpracuje příkaz switch s řetězci.  Po přechodu na příkaz switch je zjištěn proměnný rozhodovací řetězec, podle jehož hodnoty mají být zvoleny sekce case (switch(myVariable1 +myVariable2) ). Poté je volána metoda String. IsInterned, jíž je předán jako argument rozhodovací řetězec. Když metoda vrátí null, je jisté, že žádná sekce case nevyhovuje rozhodovacímu řetězci, protože všechny řetězce u case sekcí byly přidány do hashovací tabulky při JIT kompilaci. Jestliže metoda vrátí odkaz na řetězec, je testována shoda vráceného odkazu s odkazy řetězců u jednotlivých sekcí case. Když se reference řetězců shodují, je zvolena daná sekce case. Porovnávány jsou jen adresy řetězců v paměti (odkazy, reference), nikdy ne jednotlivé znaky, a proto rychlost provedení příkazu switch není nijak determinována délkou řetězců.

 



Monday, 31 May 2004 20:39:00 (Central Europe Standard Time, UTC+01:00)       
Comments [5]  .NET Framework


Tuesday, 19 July 2005 11:01:53 (Central Europe Standard Time, UTC+01:00)
"Z umístění do hashovací tabulky jsou při JIT kompilaci vyloučeny řetězce, jež jsou konstruovány dynamicky za běhu programu."
Takže keď porovnávam raťazce konštruované za behu programu(napríklad vstup ...
Tuesday, 19 July 2005 11:01:53 (Central Europe Standard Time, UTC+01:00)
Ne, ne:) Prectete si jeste jednou clanek.:)

Retezce v case sekcich jsou VZDY po startu ulozeny v hashivaci tabulce. Vstup uzivatele je predan metode IsInterned - ta vrati null, kdyz retezec v hashovaci tabulce neni, jinak vrati referenci na ...
Tuesday, 19 July 2005 11:01:53 (Central Europe Standard Time, UTC+01:00)
dakujem. este jedna dodatocna otazka ak mozem? Ak porovnavam dva dynamicke retazce, napr. dva vstupy uzivatelov tak su retazce porvnavane znak po znaku? (odpoved tusim, ale pre istotu sa radsej pytam:-))

PS: Super clanok
Tuesday, 19 July 2005 11:01:53 (Central Europe Standard Time, UTC+01:00)
Diky za pochvalu. Pokud se jedna o 2 dynamicke retezce, je k jejich porovnani pouzita metoda Equals. Staticka metod Object.Equals zjisti, ze objekty nemaji shodne reference a proto zavola instancni metodu Equals. U ni jiz bude provadeno porovnavani ...
Tuesday, 19 July 2005 11:01:53 (Central Europe Standard Time, UTC+01:00)
Switch take zacina pracovat u vyssich poctu pripadu druhou variantou. Sam si vytvori hashovaci tabulku do ktere vlozi jednotlive pripady(case) s retezcem jako klicem a linearne vzrustajici cislem jako hodnotou. Switch se pak potom interne zvrhne ...
Comments are closed.