В .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, асинхронного программирования и делегатов лямбды это не кажется правильным.
И да, мы действительно можем изменить культуру во время работы приложения, но это еще одна история.
Знаете ли вы о какой-либо настройке, свойстве или конфигурации, которую мы должны инициализировать для отслеживания?
Ответы
Ответ 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 для перевода