June 2008 - Posts

3... 2... 1... LAUNCH!

Sveiki!

Pienācis laiks vēl vienam mini ierakstam. Šoreiz par palaistuvēm programmām, kas aizvieto standarta "Start" -> "Run" funkcionalitāti. Kādu laiku atpakaļ sapratu, ka būtu ļoti jauki, ja zinot programmas (vai saīsnes) nosaukumu, to varētu ātri palaist, nemeklējot to "Start" izvēlnē. Nedaudz pameklēju un atradu lielisku rīku, sauktu par Launchy. Šo rīku izmantoju apmēram mēnesi un jāatzīst, ka esmu ļoti apmierināts ar piedāvāto funkcionalitāti, kas ļauj strādāt efektīvāk.

launchy

Ejiet... mācīties.

Izrādās, ka MSDN piedāvā jaunu iespēju - virtuālo laboratoriju, kas paralēli MSDN raksta lasīšanai ļauj spēlēties ar piemēra kodu. Viss šis brīnums izmanto remote desktop (kā pārlūka ActiveX kontroli), līdz ar to, manuprāt, efektīvāk būtu spēlēties izmantojot uz savas mašīnas uzinstalētu studiju. Tomēr pieļauju, ka atradīsies cilvēki, kam šāda iespēja liksies lietderīga.

Parunāsim par kultūru

Labvakar! Labrīt!

Šoreiz vēlos pastāstīt par .NET platformas nostūri, kurš reizēm tiek nepamatoti piemirsts un nesaprasts - formatējumu realizācijas un kultūras informācija. Domāju, ka neesmu vienīgais, kuram ir gadījies sastapties ar koda analīzes nosacījumu, kurš pieprasa norādīt formatēšanas realizācijas interfeisu (IFormatProvider). Droši vien arī ne vienīgais, kuram šis nosacījums ir izraisījis izbrīnu.

Pēc tam, kad pāris mēnešus atpakaļ izlasīju Džefa Atvuda rakstu par programmatūras internacionalizācijas problēmām, šis nosacījums kļuva nedaudz skaidrāks, bet patiesi es sapratu pēc tam, kad pāris reizes uzdūros situācijām, kad šī nosacījuma neievērošana izsauca mistiskas problēmas.

Lai ilustrētu situāciju apskatīsim šādu, vienkāršu programmu:

using System;
using System.Data;

namespace CultureDemo
{
    class Program
    {
        static void Main()
        {
            DataTable table = new DataTable("testTable");
            table.Columns.Add("value", typeof (decimal));

            Console.WriteLine("+-------+");
            Console.WriteLine("| Value\t|");
            Console.WriteLine("+-------+");

            Random r = new Random(DateTime.Now.Millisecond);
            for (int i = 0; i < 10; i++)
            {
                DataRow row = table.NewRow();
                row["value"] = (decimal)r.Next(1000) / 100;
                table.Rows.Add(row);
                Console.WriteLine("| {0:f2}\t|", row["value"]);
            }
            Console.WriteLine("+-------+");
            Console.WriteLine();

            decimal treshold = (decimal) r.Next(1000) / 100;
            int rowCount = table.Select(String.Format("value < {0}", treshold)).Length;

            Console.WriteLine("{0} rows under treshold {1}", rowCount, treshold);
        }
    }
}

Programma nedara neko sarežģītu - izveido datu tabulu, aizpilda ar 10 patvaļīgiem decimāldaļskaitļiem, izvēlas kādu patvaļīgu robežpunktu un saskaita cik ieraksti ir ar vērtību, kas mazāka par robežpunktu. Tomēr pat tik triviāla programma "izgāžas" ar eksepciju, ja to darbina uz datora, kuram reģionālajos iestatījumos ir uzstādītas Latvijai raksturīgās vērtības.

Screenshot showing exception

Papētot sīkāk izrādās, ka problēma ir rindā, kurā ģenerē atlases kritērijus, konkrētāk tajā apstāklī, ka Latvijā kā decimālais atdalītājs tiek lietots komats (un tas pēc noklusējuma tiek izmantots String.Format un citās, līdzīgās metodēs), bet .NET rindu filtrā sagaida, ka decimālais atdalītājs būs punkts. Šādos gadījumos palīgā nāk invariantā kultūra CultureInfo.InvariantCulture. Tātad, izmainītais kods:

using System;
using System.Data;
using System.Globalization;

namespace CultureDemo
{
    class Program
    {
        static void Main()
        {
            DataTable table = new DataTable("testTable");
            table.Columns.Add("value", typeof (decimal));

            Console.WriteLine("+-------+");
            Console.WriteLine("| Value\t|");
            Console.WriteLine("+-------+");

            Random r = new Random(DateTime.Now.Millisecond);
            for (int i = 0; i < 10; i++)
            {
                DataRow row = table.NewRow();
                row["value"] = (decimal)r.Next(1000) / 100;
                table.Rows.Add(row);
                Console.WriteLine("| {0:f2}\t|", row["value"]);
            }
            Console.WriteLine("+-------+");
            Console.WriteLine();

            decimal treshold = (decimal) r.Next(1000) / 100;
            int rowCount = table.Select(String.Format(CultureInfo.InvariantCulture, "value < {0}", treshold)).Length;

            Console.WriteLine("{0} rows under treshold {1}", rowCount, treshold);
        }
    }
}

Un rezultāts:

cultureinfo_normal

Līdzīgas problēmas (un risinājums) ir gadījumos, kad jāveic atlase pēc datuma. Tad izmantojam šo pašu recepti, lai izvairītos no problēmām.

Ja ar laiku pierod norādīt formatēšanas realizāciju izsaucot dažādas metodes datu noformēšanā, tad var nonākt otrā galējībā, t.i., visur sākt norādīt invarianto kultūru. Jāatzīmē, ka tas nebūs īsti korekti, jo pareizi būtu lietotājam informāciju attēlot tādā formātā, kāda tas ir pieradis. Tāpēc gadījumos, kad datus formē parādīšanai lietotājam, pareizi būtu izmantot System.Threading.Thread.CurrentThread.CurrentUICulture, kā formatēšanas realizāciju.

 


Nolēmu atjaunot zemsvītras piezīmes. Šajās varu pastāstīt, ka ir izdevies ciest no saulītes ar zobiem un tagad sēžu ar sūrstošu muguru :)