CultureInfo w C#

Wiele razy stykam się z sytuacją, że gdy ReSharper podpowiada programistom, że należy użyć w metodzie ToString parametru CultureInfo, każdy wstawia tam najbezpieczniejsza wersję czyli InvariantCulture. A może czasem warto wstawić coś innego? Poniżej opisuje, czym jest CultureInfo i jak go używać.

CultureInfo

Klasa CultureInfo jest używana między innymi wtedy gdy chcemy rozwiązać problem używania odpowiednich separatorów dziesiętnych, formatów dat i końcówek walutowych. Używana jest również podczas wykonywania porównań między stringami.

Istnieje 354 zdefiniowanych rodzajów kultur, które określają wyżej wymienione elementy(separatory, walut, daty itd.)

Poniżej przedstawienie przykładowych różnic z różnych kultur:


            var polishFormat = new CultureInfo("pl-PL");

            var englishFormat = new CultureInfo("en-GB");

            var americanFormat = new CultureInfo("en-US");

            decimal d = 12.232M;

            Console.WriteLine("Polskie formatowanie: {0}", d.ToString("C", polishFormat));
            Console.WriteLine("Angielskie formatowanie: {0}", d.ToString("C", englishFormat));
            Console.WriteLine("Amerykańskie formatowanie: {0} ", d.ToString("C", americanFormat));

Wynik prezentuje się następująco:

cultureinfo

Widać wyraźnie, że w zależności od użytej kultury zmienia się separator dziesiętny jak i opis waluty.

Najważniejszym zastosowaniem ustawiania jednej konkretniej kultury do której formatujemy dane  jest wyświetlanie danych na ekranie użytkownikowi końcowemu. Może istnieć przypadek, że będzie zależało Nam aby użytkownik widział dane dokładnie w takim formacie w jakim on sam wybrał lub jaki my uważamy za słuszny.

CurrentCulture

Innym sposobem określenia kultury jest odczytanie kultury, która ustawiona jest na komputerze użytkownika (Panel sterowania-> Ustawienia regionalne). Aby to zrobić wystarczy użyć właściwości klasy CurrentInfo zwanej CurrentCulture. Przykład poniżej:


 decimal d = 12.232M;

            Console.WriteLine("Formatowanie na komputerze: {0}", CultureInfo.CurrentCulture);
            Console.WriteLine("Formatowanie: {0}", d.ToString("C", CultureInfo.CurrentCulture));

Jedyne o czym należy pamiętać, że CurrentCulture nie zawsze wyświetla te ustawienia, które są ustawione w komputerze na którym działa aplikacja. Szczegóły są tutaj: http://msdn.microsoft.com/pl-pl/library/system.globalization.cultureinfo.currentculture%28v=vs.110%29.aspx

Generalnie trzeba pamiętać, że CurrentCulture jest brane z wątku, który może działać w osobnej domenie co może prowadzić do tego, że wyświetli się jednak inna kultura niż sądziliśmy. Podobnie jest z aplikacjami serwerowymi, dla których CurrentCulture będzie ustawieniami serwera a nie aplikacji użytkownika.

Dodatkowo w firmach i korporacjach często miałem przypadki, że na różnych komputerach w jednej firmie użytkownicy mieli różne ustawienia regionalne więc ta właściwość na nie wiele się przydawała.

InvariantCulture

Trzecią możliwością jest używanie właściwości klasy CultureInfo jaką jest InvariantCulture.

InvariantCulture nie jest żadną konkretną reprezentacją jakiejś jednej kultury. Jest to raczej najbardziej standardowa i ujednolicona forma formatowania oparta na kulturze typu „en-US” (ale nie jest taka sama)

Najważniejszym zastosowaniem InvariantCulture  jest zapis i odczyt danych. Gdy mam zapisać lub odczytać dane z/do pliku lub bazy używam właśnie InvariantCulture  to dobry sposób aby uniknąć problemów z separatorem daty na przykład.

Używam InvariantCulture również wtedy kiedy wykonuje parsowanie dat lub liczb w metodach Parse,  ParseExact lub TryParse

Poniżej prosty przykład pokazujący działanie InvariantCulture .


        var englishFormat = new CultureInfo("en-GB");

            decimal d = 12.632M;

            Console.WriteLine("Moje ustawienia regionalne: {0}", CultureInfo.CurrentCulture);

            Console.WriteLine("Mój separator: {0}", CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);

            Console.WriteLine("Format liczby na dla moich ustawień: {0}", d.ToString(CultureInfo.CurrentCulture));

            var text = d.ToString(englishFormat);

            Console.WriteLine("Liczba, którą odebrałem z bazy lub pliku : {0}", text);

            double convertToDouble = Convert.ToDouble(text);

            Console.WriteLine("Liczba po konwersjii: {0}", convertToDouble);

Czy ten kod się skompiluje?

Mamy jasno podane dane, że ustawienia regionalne dla tego komputera(wątku) są ustawione na „pl-PL”. Separator dla moich ustawień to przecinek. Symulujemy odebranie liczby w formacie „en-GB” gdzie separatorem dziesiętnym jest kropka. Następnie wykonujemy prostą konwersję ze string-a do decimal-a. W tym miejscu pewnie ReSharper podpowie, że należy użyć klasy CultureInfo.

cultureinfo_2

Jak widać kod się nie skompiluje ponieważ, separatorem daty dla ustawień „pl-PL”, które posiadam jest przecinek (który oczywiście można w ustawieniach regionalnych zmienić na inny ale nie o to chodzi) wiec konwersja liczby z separatorem w postaci kropki jest nie możliwa.

Aby kod działał należy użyć oczywiście InvariantCulture


 double convertToDouble = Convert.ToDouble(text, CultureInfo.InvariantCulture);

Po zmianie konwersja się udaje a wyniki są następujące:

cultureinfo_3

Podsumowując:

InvariantCulture – używamy do zapisu i odczytu danych oraz do operacji wew. programu. Mamy wtedy pewność, że format na którym pracujemy jest jednolity.

CultureInfo.CurrentCulture – używamy gdy chcemy sformatować dane od użytkownika na przykład z formularzy itd. Odczytywanie CurrentCulture nie zawsze jest prawdziwe i trzeba na to zwrócić uwagę.

Specyficznych kultur używamy gdy chce na siłę wymusić format na przykład wyświetlając dane użytkownikowi końcowemu.