Подтвердить что ты не робот

В .Net: лучший способ сохранить CurrentCulture в новой теме?

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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


EDIT: эта проблема была исправлена ​​в .NET 4.5 с помощью свойств CultureInfo.DefaultThreadCurrentCulture и DefaultThreadCurrentUICulture.


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

Ответ 2

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

И я действительно не понимаю чувства OP, что "не нужно" писать код, чтобы скопировать материал, который он хочет скопировать. Возможно, вы захотите разместить этот код где-нибудь, где его можно повторно использовать, но, кроме этого, я действительно не вижу проблемы с кодом. В моей книге это более прямолинейно и замечательно, чем любое выражение лямбды, которое я когда-либо видел, и это сделало бы работу совершенно красиво. Написание причудливого кода ради того, чтобы он был фантазией, по крайней мере, не мой стиль.

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

// 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

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

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

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

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

Ответ 4

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

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());
        }

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