Wielowątkowość a CultureInfo

CultureInfo zawiera informacje regionalne, przydatne, jeśli chcemy dostosować naszą aplikację do różnym krajów. Używamy tej klasy m.in. do określenia formatowania liczb, dat czy po prostu języka w jakim wyświetlamy tekst.

Jeśli korzystamy z domyślnych ustawień regionalnych, wtedy poniższy kod wyświetli prawidłową wartość CultureInfo:

static void Main(string[] args)
{
  DisplayCulture();
  Task.Factory.StartNew(DisplayCulture);

  Console.ReadLine();
}

private static void DisplayCulture()
{
  Console.WriteLine(CultureInfo.CurrentCulture.ToString());
}

Często jednak w aplikacji można zmienić język i tym samym CultureInfoUI. Nierzadko popełnianym błędem jest po prostu ustawienie CultureInfo dla aktualnego wątku:

class Program
{
   static void Main(string[] args)
   {
       Thread.CurrentThread.CurrentCulture=new CultureInfo("en-US");
       DisplayCulture();
       Task.Factory.StartNew(DisplayCulture);

       Console.ReadLine();
   }

   private static void DisplayCulture()
   {
       Console.WriteLine(Thread.CurrentThread.CurrentCulture);
   }
}

Kod zmienia kulturę wyłącznie aktualnego wątku – w tym przypadku głównego. Wszelkie inne wątki, będą używały kultury domyślnej z ustawień regionalnych. W celu zsynchronizowania wątków musimy sami przekazywać kulturę i ustawiać ją ręcznie w każdym z wątków.

Jest to dość czasochłonne i nudne zajęcie więc istnieją pewne usprawnienia od .NET 4.5:

class Program
{
   static void Main(string[] args)
   {
       CultureInfo.DefaultThreadCurrentCulture=new CultureInfo("en-US");
       DisplayCulture();
       Task.Factory.StartNew(DisplayCulture);

       Console.ReadLine();
   }

   private static void DisplayCulture()
   {
       Console.WriteLine(Thread.CurrentThread.CurrentCulture);
   }
}

W .NET 4.5 dodano DefaultThreadCurrentCulture. Ustawienie tej właściwości spowoduje, że wszelkie wątki będą korzystały ze wskazanej kultury. Przed .NET 4.5 trzeba było ręcznie to ustawiać albo zastosować hack, który znajdziecie tutaj:

http://blog.rastating.com/setting-default-currentculture-in-all-versions-of-net/

Kod:

public void SetDefaultCulture(CultureInfo culture)  
{
    Type type = typeof(CultureInfo);

    try
    {
        type.InvokeMember("s_userDefaultCulture",
                            BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                            null,
                            culture,
                            new object[] { culture });

        type.InvokeMember("s_userDefaultUICulture",
                            BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                            null,
                            culture,
                            new object[] { culture });
    }
    catch { }

    try
    {
        type.InvokeMember("m_userDefaultCulture",
                            BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                            null,
                            culture,
                            new object[] { culture });

        type.InvokeMember("m_userDefaultUICulture",
                            BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                            null,
                            culture,
                            new object[] { culture });
    }
    catch { }
}

Warto zwrócić na to uwagę, ponieważ wyświetlanie np. dat w wątku głównym w inny sposób niż w pozostałych wątkach, nie jest dobrym pomysłem.

One thought on “Wielowątkowość a CultureInfo”

  1. Co prawda miałem do tego celu własny statyczny wrapper dla Threads i ThreadPool, ale teraz on mi się bardzo uprościł.

Leave a Reply

Your email address will not be published.