В .Net: лучший способ сохранить CurrentCulture в новой теме? - PullRequest
24 голосов
/ 28 февраля 2011

В проекте .Net 4.0 WPF нам нужно сохранить ту же самую CurrentCulture в каждом потоке, что и в главном потоке.

Учитывая, мы можем инициализировать культуру нового потока с помощью кода, подобного следующему:

  1. Хранить информацию в переменной (контексте)

    context.CurrentCulture = Thread.CurrentThread.CurrentCulture;
    context.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
    
  2. В новом потоке инициализировать из сохраненного контекста

    Thread.CurrentThread.CurrentCulture = context.CurrentCulture;
    Thread.CurrentThread.CurrentUICulture = context.CurrentUICulture;
    

Но в наш век TPL, асинхронного программирования и лямбда-делегатов это не кажется правильным.

И да, мы можем изменить культуру во время работы приложения, но это уже другая история.

Вам известны какие-либо настройки, свойства или настройки, которые мы должны инициализировать для отслеживания?

Ответы [ 4 ]

25 голосов
/ 28 февраля 2011

Нет хорошего пути, избегайте этого любой ценой.Основная проблема заключается в том, что культура не является частью Thread.ExecutionContext, она не переходит из одного потока в другой.Это неразрешимая проблема, культура - это свойство нити Windows.Он всегда будет инициализирован в соответствии с культурой системы, выбранной в приложении «Язык и региональные стандарты» панели управления.

Внесение временных локальных изменений в культуру - это нормально, попытка переключить «процесс» на другую культуру - источник ошибок, на которые вы будете охотиться месяцами.Порядок сортировки строк является самым неприятным источником проблем.


РЕДАКТИРОВАТЬ: эта проблема была исправлена ​​в .NET 4.5 со свойствами CultureInfo.DefaultThreadCurrentCulture и DefaultThreadCurrentUICulture.


EDIT:и получил реальное решение в .NET 4.6, культура теперь перетекает из одного потока в другой.Проверьте статью MSDN для CultureInfo.CurrentCulture для деталей.Помните, что описание не совсем соответствует поведению, требуется тестирование.

5 голосов
/ 28 февраля 2011

Я не понимаю, почему мистер Пассант предупреждает об этом и одновременно говорит, что это нормально - делать «временные» «локальные» изменения в культуре. Как мы видели, ни одна культура потоков не является действительно локальной для потоков - она ​​доступна всем, кто может ссылаться на поток через открытые свойства. И если это нормально, чтобы изменить его на короткое время, то почему не нормально, чтобы изменить его на более длительное время? Где вы пересекаете черту и почему?

Также я не совсем понимаю, что ОП считает, что «не должно быть необходимости» писать код для копирования того, что он хочет скопировать. Возможно, вы захотите поместить этот код где-нибудь, где вы сможете использовать его повторно, но кроме этого я действительно не вижу проблемы с кодом. В моей книге это гораздо более просто и замечательно, чем любое лямбда-выражение, которое я когда-либо видел, и оно отлично справилось бы с этой работой. Написание причудливого кода ради причудливости, по крайней мере, не в моем стиле.

Вы можете сделать что-то вроде этого:

// Program.cs
static CultureInfo culture, uiCulture;

[STAThread]
static public void Main()
{
   var t = Thread.CurrentThread;
   culture = t.CurrentCulture;
   uiCulture = t.CurrentUICulture;
}

static public Thread CreateThread() 
{
    return new Thread() { CurrentCulture = culture, CurrentUICulture = uiCulture }; }
}
3 голосов
/ 28 февраля 2011

Кстати, до меня дошло, что, хотя культура по умолчанию происходит из «Региональных настроек» Windows, она, вероятно, может быть переопределена в конфигурации .NET.

Если бы это было приложение ASP.NET, вы бы использовали элемент globalization в web.config. Я бы, наверное, начал где-то здесь, чтобы узнать о локализации в WPF:

http://msdn.microsoft.com/en-us/library/ms788718.aspx#workflow_to_localize

(Это я или Microsoft использует слова глобализация и локализация, довольно запутанно, взаимозаменяемо?)

2 голосов
/ 27 января 2012

Когда я создаю задачи с помощью TPL, я всегда передаю культуру из текущего потока пользовательского интерфейса.См. Пример кода ниже.

private void WorkProcessingAsync(IWorkItem workItem)
        {
            IsBusy = true;
            /* =============================
            *  Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation / globalisation / Multilanguate features in Background Thread
            * ==============================*/
            Task<IWorkItem> task = Task.Factory.StartNew((stateObj) =>
            {
                // here we are already in the task background thread
                // save cast the given stateObj
                var tuple = stateObj as Tuple<IWorkItem, CultureInfo>;

                Debug.Assert(tuple != null, "tuple != null");

                Thread.CurrentThread.CurrentUICulture = tuple.Item2;   // Here we set the UI-Thread Culture to the Background Thread

                var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1);
                return longRunningOperationAnswer;

            }, new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture));  // here we pass the UI-Thread Culture to the State Object



            /* =======================================================================
            *   Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread
            * =======================================================================*/
            task.ContinueWith((t) =>
            {
                IsBusy = false;
                // handle longRunningOperationAnswer here in t.Result
                Log.Debug("Operation completet with {0}", t.Result);

            }, CancellationToken.None
            , TaskContinuationOptions.OnlyOnRanToCompletion
            , TaskScheduler.FromCurrentSynchronizationContext());

            /* =======================================================================
         *   Handle OnlyOnFaulted Task back in UiThread
         * =======================================================================*/
            task.ContinueWith((t) =>
            {
                IsBusy = false;
                AggregateException aggEx = t.Exception;

                if (aggEx != null)
                {
                    aggEx.Flatten();
                    Log.ErrorFormat("The Task exited with Exception(s) \n{0}", aggEx);
                    foreach (Exception ex in aggEx.InnerExceptions)
                    {
                        if (ex is SpecialExaption)
                        {
                            //Handle Ex here
                            return;
                        }
                        if (ex is CustomExeption)
                        {
                            //Handle Ex here
                            return;
                        }
                    }
                }
            }, CancellationToken.None
            , TaskContinuationOptions.OnlyOnFaulted
            , TaskScheduler.FromCurrentSynchronizationContext());
        }

Мне нужно было пройти обучение, чтобы найти правильный файл translation.resx для перевода

...