CultureInfo.ClearCachedData не работает должным образом в .net 4.6 и старше только для WPF

Рассмотрим следующее простое приложение WPF:

XAML (главное окно):

<StackPanel>
    <TextBlock x:Name="Old"/>
    <TextBlock x:Name="New"/>
</StackPanel>

Код программной части:

  public MainWindow()
        {
            InitializeComponent();
            SystemEvents.UserPreferenceChanged += (sender, args) =>
            {
                Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
                CultureInfo.CurrentCulture.ClearCachedData();
                New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
            };
        }

Каждый раз, когда пользователь меняет свои региональные настройки, он будет печатать в Old TextBlock имя старой культуры, а после вызова ClearCachedData имя новой культуры будет установлено в New TextBlock. Это окно, в котором я изменяю эти настройки в разделе «Часы и регион» панели управления:

введите здесь описание изображения

Теперь, если вы создадите таргетинг на версию 4.5.2 или более раннюю, она будет работать так, как ожидалось, и после фактического изменения настройки вы получите вывод, подобный этому:

введите здесь описание изображения

Теперь, собрав версию 4.6 или более позднюю, вы получите что-то вроде этого:

введите здесь описание изображения

Если вы ориентируетесь на 4.6 или более позднюю версию, независимо от того, сколько раз вы меняете регион числового формата, независимо от того, сколько раз вы вызываете ClearCachedData, CurrentCulture не изменится...

Но ждать! Вещи становятся еще более странными... Если вы поместите приведенный выше код в консольное приложение (используя Console.WriteLine вместо, конечно), он будет работать независимо от того, какую структуру вы выбрали (4.5.2, 4.6, что угодно..), странно не Это? Вот почему я написал «Только WPF» в заголовке.. но я точно не знаю, происходит ли это на какой-либо другой платформе, кроме WPF.

В этой истории есть еще одна сумасшедшая вещь, так как я играл с этой проблемой со вчерашнего дня, я нашел хитрый способ заставить WPF или консольные приложения, ориентированные на 4.5.2 или ниже, работать неправильно, как и приложение, ориентированное на 4.6. и старше. Простая установка CultureInfo.DefaultThreadCurrentCulture на что-либо отличное от нуля приведет к поломке.

public MainWindow()
{
    InitializeComponent();

    CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;

    SystemEvents.UserPreferenceChanged += (sender, args) =>
    {
        Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
        CultureInfo.CurrentCulture.ClearCachedData();
        New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
    };
}

Приведенный выше код будет вести себя так, как если бы он был ориентирован на 4.6. Итак, я подумал, что WPF, нацеленный на 4.6, каким-то образом устанавливал CultureInfo.DefaultThreadCurrentCulture, но после проверки этой теории я действительно застрял, потому что по умолчанию он равен нулю, событие в 4.6 и старше...

Итак, это ошибка в WPF и/или .NET 4.6? Я не смог найти сообщение об этом критическом изменении.

Пожалуйста, все, что проливает свет на это, будет оценено. Кроме того, если это ошибка, и кто-нибудь знает, где я могу сообщить об этом, пожалуйста, сообщите мне.

Примечание. Я помечаю этот вопрос как C#, так как это язык, который я использую, хотя я считаю, что эта проблема не зависит от языка.


person taquion    schedule 18.12.2018    source источник
comment
Что ж, большие большие изменения в .NET 4.6. Он исправляет старый проблема с культурой, не перетекающей из одного потока в другой. Если его реализация вызывает эту проблему, не столь очевидную для меня, рассмотрите возможность использования AppContextSwitchOverride в файле app.config, чтобы отказаться.   -  person Hans Passant    schedule 18.12.2018
comment
@HansPassant, спасибо за помощь. Я выполнил тест, установив значение true Switch.System.Globalization.NoAsyncCurrentCulture, я подтверждаю, что переключатель был включен, вызвав AppContext.TryGetSwitch. К сожалению, у меня не получилось...   -  person taquion    schedule 18.12.2018
comment
Кстати... я не думаю, что это напрямую связано с .net 4.6, так как оно работает в консольном приложении.   -  person taquion    schedule 18.12.2018


Ответы (1)


Для тех посетителей из будущего и за его пределами, у которых может возникнуть такая же проблема. Я нашел своего рода обходной путь:

    public MainWindow()
    {
        InitializeComponent();

        CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;

        SystemEvents.UserPreferenceChanged += (sender, args) =>
        {
            Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
            CultureInfo.CurrentCulture.ClearCachedData();
            CultureInfo.CurrentCulture = new Thread(() => { }).CurrentCulture;
            New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
        };
    }

Приведенный выше код будет работать в обоих вариантах сборки: 4.5.2 и ранее или 4.6 и выше. Обратите внимание, что вызов ClearCachedData по-прежнему обязателен. Мы создаем новый экземпляр потока и вручную обновляем нашу CurrentCulture до экземпляра нового потока.

Удачного кодирования!

person taquion    schedule 20.12.2018