设置默认的CurrentCulture和CurrentUICulture(.NET 4.5.2和.NET 4.6之间的差异)

时间:2016-03-30 15:01:53

标签: c# .net wpf

我有一个WPF应用程序,我想在整个应用程序中修改文化设置。这是一个简化的演示,演示了我想要实现的目标:

using System.Windows;

namespace CultureProblem3
{
    public partial class MainWindow : Window
    {
        private System.Text.StringBuilder _sb;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void btn_Action_Click(object sender, RoutedEventArgs e)
        {
            var ci = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name);
            ci.NumberFormat.NumberGroupSeparator = "xxx";
            System.Globalization.CultureInfo.DefaultThreadCurrentCulture = ci;
            System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = ci;

            _sb = new System.Text.StringBuilder();

            WriteInfo(_sb, "DefaultThreadCurrentCulture", System.Globalization.CultureInfo.DefaultThreadCurrentCulture);
            WriteInfo(_sb, "DefaultThreadCurrentUICulture", System.Globalization.CultureInfo.DefaultThreadCurrentUICulture);
            WriteInfo(_sb, "CultureInfo.CurrentCulture", System.Globalization.CultureInfo.CurrentCulture);
            WriteInfo(_sb, "CultureInfo.CurrentUICulture", System.Globalization.CultureInfo.CurrentUICulture);
            WriteInfo(_sb, "CurrentThread.CurrentCulture", System.Threading.Thread.CurrentThread.CurrentCulture);
            WriteInfo(_sb, "CurrentThread.CurrentUICulture", System.Threading.Thread.CurrentThread.CurrentUICulture);

            var t = new System.Threading.Thread(DoWork);
            t.Start();
            System.Threading.Thread.Sleep(1000);

            MessageBox.Show(_sb.ToString());
        }

        private void DoWork()
        {
            WriteInfo(_sb, "CultureInfo.CurrentCulture - another thread", System.Globalization.CultureInfo.CurrentCulture);
            WriteInfo(_sb, "CultureInfo.CurrentUICulture - another thread", System.Globalization.CultureInfo.CurrentUICulture);
            WriteInfo(_sb, "CurrentThread.CurrentCulture - another thread", System.Threading.Thread.CurrentThread.CurrentCulture);
            WriteInfo(_sb, "CurrentThread.CurrentUICulture - another thread", System.Threading.Thread.CurrentThread.CurrentUICulture);
        }

        private void WriteInfo(System.Text.StringBuilder sb, string desc, System.Globalization.CultureInfo ci)
        {
            sb.AppendLine($"{desc}: {ci.NumberFormat.NumberGroupSeparator}");
        }
    }
}

它似乎有用,但并非总是如此。当我以.NET 4.5.2为目标并在Windows 7上运行app,在Windows 10上运行.NET 4.5.2或在Windows 7上运行.NET 4.6时,输出(显示在消息框中):

DefaultThreadCurrentCulture: xxx
DefaultThreadCurrentUICulture: xxx
CultureInfo.CurrentCulture: xxx
CultureInfo.CurrentUICulture: xxx
CurrentThread.CurrentCulture: xxx
CurrentThread.CurrentUICulture: xxx
CultureInfo.CurrentCulture - another thread: xxx
CultureInfo.CurrentUICulture - another thread: xxx
CurrentThread.CurrentCulture - another thread: xxx
CurrentThread.CurrentUICulture - another thread: xxx

但是当我以.NET 4.6为目标并在Windows 10上运行app时,我得到了以下内容:

DefaultThreadCurrentCulture: xxx
DefaultThreadCurrentUICulture: xxx
CultureInfo.CurrentCulture: ,
CultureInfo.CurrentUICulture: ,
CurrentThread.CurrentCulture: ,
CurrentThread.CurrentUICulture: ,
CultureInfo.CurrentCulture - another thread: ,
CultureInfo.CurrentUICulture - another thread: ,
CurrentThread.CurrentCulture - another thread: ,
CurrentThread.CurrentUICulture - another thread: ,

有人可以解释一下我的区别吗?我已经检查了this .NET 4.6 compatibility list,但我找不到任何可能影响它的东西(或者我是盲目的)。我假设最近的相关变化是CultureInfo.CurrentCulture和CultureInfo.CurrentUICulture属性现在是读写而不是只读(source)。

我知道我只设置了DefaultThreadCurrentCulture / DefaultThreadCurrentUICulture而没有显式设置CurrentCulture / CurrentUICulture。但是according to MSDN,我想我的情况没问题(如果我错了请纠正我):

  

如果您尚未明确设置任何现有线程的文化   在应用程序域中执行,设置   DefaultThreadCurrentCulture属性也会改变这些文化   线程。但是,如果这些线程在另一个应用程序中执行   域,它们的文化由DefaultThreadCurrentCulture定义   该应用程序域中的属性,或者,如果没有默认值   由默认的系统文化定义。因此,我们建议   您始终明确设置主应用程序的文化   线程,而不是依赖于DefaultThreadCurrentCulture属性   定义主应用程序线程的文化。

另外,这并不能解释不同操作系统之间的行为差​​异,还是它呢?

有趣的是,如果我更换

var ci = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name);
ci.NumberFormat.NumberGroupSeparator = "xxx";
System.Globalization.CultureInfo.DefaultThreadCurrentCulture = ci;
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = ci;

var ci = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name);
ci.NumberFormat.NumberGroupSeparator = "xxx";
System.Globalization.CultureInfo.DefaultThreadCurrentCulture = ci;
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = ci;

var ci2 = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name);
ci2.NumberFormat.NumberGroupSeparator = "yyy";
System.Threading.Thread.CurrentThread.CurrentCulture = ci2;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci2;

输出如下:

Windows 7上的.NET 4.5.2或Windows 10上的.NET 4.5.2:

DefaultThreadCurrentCulture: xxx
DefaultThreadCurrentUICulture: xxx
CultureInfo.CurrentCulture: yyy
CultureInfo.CurrentUICulture: yyy
CurrentThread.CurrentCulture: yyy
CurrentThread.CurrentUICulture: yyy
CultureInfo.CurrentCulture - another thread: xxx
CultureInfo.CurrentUICulture - another thread: xxx
CurrentThread.CurrentCulture - another thread: xxx
CurrentThread.CurrentUICulture - another thread: xxx

Windows 7上的.NET 4.6或Windows 10上的.NET 4.6:

DefaultThreadCurrentCulture: xxx
DefaultThreadCurrentUICulture: xxx
CultureInfo.CurrentCulture: yyy
CultureInfo.CurrentUICulture: yyy
CurrentThread.CurrentCulture: yyy
CurrentThread.CurrentUICulture: yyy
CultureInfo.CurrentCulture - another thread: yyy
CultureInfo.CurrentUICulture - another thread: yyy
CurrentThread.CurrentCulture - another thread: yyy
CurrentThread.CurrentUICulture - another thread: yyy

.NET 4.5.2的行为是我的预期,我认为.NET 4.6的行为是错误的。有趣的是,我发誓几天前工作“很好”(虽然我无法100%确认)。我只是在猜测,但有些Windows Update无法改变这种行为吗?

1 个答案:

答案 0 :(得分:6)

从.NET 4.6开始,来自正在运行的线程的CurrentCulture将流向它创建的新线程,并在执行排队项时流向ThreadPool线程。您可以在MSDN上阅读有关缓解此更改的更多信息:Mitigation: Culture and Asynchronous Operations

以下是上述文档中列出的用于切换回旧行为的选项:

  • 将所需的CultureInfo.CurrentCulture或CultureInfo.CurrentUICulture属性显式设置为异步任务中的第一个操作

  • 启动时致电AppContext.SetSwitch("Switch.System.Globalization.NoAsyncCurrentCulture", true);

  • 覆盖.config

<configuration>
    <runtime>
        <AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true" />
    </runtime>
</configuration>