CultureInfo

本文对此 API 的参考文档进行补充说明。

CultureInfo 类提供区域性特定信息,例如语言、子语言、国家/地区、日历以及与特定区域关联的惯例。 此类还提供对 DateTimeFormatInfoNumberFormatInfoCompareInfo 的区域性特定实例和 TextInfo 对象的访问。 这些对象包含区域性特定操作所需的信息,例如大小写、设置日期和数字格式以及比较字符串。 CultureInfo 类由设置格式、分析或操作区域性特定数据(例如 StringDateTimeDateTimeOffset 和数值类型)的类直接或间接使用。

区域性名称和标识符

CultureInfo 类根据 RFC 4646 为每个区域性指定唯一的名称。 该名称是一个与语言相关的 ISO 639 双小写或三小写字母的区域性代码和一个与国家/地区相关的 ISO 3166 双大写字母子区域性代码的组合。 此外,对于在 Windows 10 或更高版本下运行的应用,支持对应于有效 BCP-47 语言标记的区域性名称。

注意

将区域性名称传递给类构造函数或方法(如 CreateSpecificCultureCultureInfo)时,其大小写并不重要。

基于 RFC 4646 的区域性名称的格式是 languagecode2-country/regioncode2,其中 languagecode2 是双字母语言代码,country/regioncode2 是双字母子区域性代码。 示例包括日语(日本)的 ja-JP 和英语(美国)的 en-US。 在双字母语言代码不可用的情况下,使用 ISO 639-3 中定义的三字母代码。

某些区域性名称还指定 ISO 15924 脚本。 例如,Cyrl 指定西里尔语脚本,Latn 指定拉丁语脚本。 包含脚本的区域性名称使用模式 languagecode2-scripttag-country/regioncode2。 此类区域性名称的一个示例是乌兹别克语的 uz-Cyrl-UZ(西里尔语,乌兹别克斯坦)。 在 Windows Vista 之前的 Windows 操作系统上,包含脚本的区域性名称使用模式 languagecode2-country/regioncode2-scripttag,例如,乌兹别克语的 uz-UZ-Cyrl(西里尔语,乌兹别克斯坦)。

非特定区域性仅由双字母小写语言代码指定。 例如,fr 指定法语的非特定区域性,de 指定德语的非特定区域性。

注意

有两个区域性名称与此规则相矛盾。 名为 zh-Hans 的中文(简体)区域性和名为 zh-Hant 的中文(繁体)区域性是非特定区域性。 这些区域性名称表示当前标准,且应使用,除非有理由使用旧名称 zh-CHSzh-CHT

区域性标识符是标准的国际数字缩写,具有唯一地标识其中一个已安装的区域性所需的组件。 应用程序可以使用预定义的区域性标识符,或者定义自定义标识符。

某些预定义区域性名称和标识符由 System.Globalization 命名空间中的该类和其他类使用。 有关 Windows 系统的详细区域性信息,请参阅 Windows 支持的语言/区域名称列表中的“语言标记”列。 列名遵循 BCP 47 定义的标准。

区域性名称和标识符仅表示可在特定计算机上找到的区域性的子集。 Windows 版本或服务包可以更改可用的区域性。 应用程序可以使用 CultureAndRegionInfoBuilder 类添加自定义区域性。 用户可以使用 Microsoft Locale Builder 工具添加自己的自定义区域性。 Microsoft Locale Builder 是使用 CultureAndRegionInfoBuilder 类以托管代码编写的。

多个不同名称与区域性密切相关,尤其是与以下类成员关联的名称:

固定区域性、非特定区域性和特定区域性

这些区域性通常分为三组:固定区域性、非特定区域性和特定区域性。

固定区域性不区分区域性。 应用程序使用空字符串("")或其标识符指定固定区域性。 InvariantCulture 定义固定区域性的实例。 它与英语相关联,但不与任何国家/地区相关联。 它用于命名空间中 Globalization 几乎所有需要区域性的方法。

非特定区域性是一种与语言(而非国家/地区)关联的区域性。 特定区域性是一种与语言和国家/地区相关联的区域性。 例如,fr 是法语区域性的非特定区域性名称,fr-FR 是法语(法国)的特定区域性名称。 请注意,中文(简体)和中文(繁体)也被视为非特定区域性。

不建议为非特定区域性创建 CompareInfo 类的实例,因为它包含的数据是任意的。 若要显示和排序数据,请同时指定语言和区域。 此外,为非特定区域性创建的 CompareInfo 对象的 Name 属性仅返回国家/地区,不包括该区域。

定义的区域性具有层次结构,其中特定区域性的父区域性是非特定区域性,非特定区域性的父区域性是固定区域性。 Parent 属性包含与特定区域性关联的非特定区域性。 自定义区域性应定义符合此模式的 Parent 属性。

如果特定区域性的资源在操作系统中不可用,则使用关联的非特定区域性的资源。 如果非特定区域性的资源不可用,则使用主程序集中嵌入的资源。 有关资源回退进程的详细信息,请参阅打包和部署资源

Windows API 中的区域设置列表与 .NET 支持的区域性列表略有不同。 如果需要与 Windows 的互操作性(例如,通过 p/invoke 机制),应用程序应使用为操作系统定义的特定区域性。 使用特定区域性可确保与对等的 Windows 区域设置保持一致,该区域设置由与 LCID 相同的区域设置标识符标识。

只能为固定区域性或特定区域性创建 DateTimeFormatInfoNumberFormatInfo,而不能为非特定区域性创建。

如果 DateTimeFormatInfo.CalendarTaiwanCalendarThread.CurrentCulture 未设置为 zh-TW,则 DateTimeFormatInfo.NativeCalendarNameDateTimeFormatInfo.GetEraNameDateTimeFormatInfo.GetAbbreviatedEraName 返回一个空字符串("")。

自定义区域性

在 Windows 上,可以创建自定义区域设置。 有关详细信息,请参阅自定义区域设置

CultureInfo 和区域性数据

.NET 根据实现、平台和版本从各种源中的一个派生其区域性数据:

  • 在 Unix 平台或 Windows 10 及更高版本上运行的所有 .NET (Core) 版本中,区域性数据由 Unicode (ICU) 库的国际组件提供。 ICU 库的特定版本取决于操作系统。
  • 在 Windows 9 和更低版本上运行的所有 .NET (Core) 版本中,区域性数据由 Windows 操作系统提供。
  • 在 .NET Framework 4 及更高版本中,区域性数据由 Windows 操作系统提供。

因此,在特定的 .NET 实现、平台或版本上可用的区域性可能在另一个 .NET 实现、平台或版本上不可用。

某些 CultureInfo 对象因基础平台而有所不同。 具体而言,zh-CN(中文(简体,中国))和 zh-TW(中文(繁体,台湾))在 Windows 系统上是可用区域性,但它们在 Unix 系统上是别名区域性。 “zh-CN”是“zh-Hans-CN”区域性的别名,“zh-TW”是“zh-Hant-TW”区域性的别名。 对 GetCultures 方法的调用不会返回别名区域性,并且可能具有与 Windows 对应项不同的属性值(包括不同的 Parent 区域性)。 对于 zh-CNzh-TW 区域性,这些差异包括:

  • 在 Windows 系统上,“zh-CN”区域性的父区域性为“zh-Hans”,“zh-TW”区域性的父区域性为“zh-Hant”。 这两种区域性的父区域性是“zh”。 在 Unix 系统上,两种区域性的父区域性都是“zh”。 这意味着,如果没有为“zh-CN”或“zh-TW”区域性提供区域性特定的资源,但为非特定区域性“zh-Hans”或“zh-Hant”提供了资源,那么应用程序将在 Windows 而非 Unix 上加载非特定区域性的资源。 在 Unix 系统上,必须将线程的 CurrentUICulture 显式设置为“zh-Hans”或“zh-Hant”。

  • 在 Windows 系统上,在表示“zh-CN”区域性的实例上调用 CultureInfo.Equals 并向其传递“zh-Hans-CN”实例将返回 true。 在 Unix 系统上,该方法调用返回 false。 此行为也适用于在“zh-TW” CultureInfo 实例上调用 Equals 并将其传递给“zh-Hant-Tw”实例。

动态区域性数据

除固定区域性外,区域性数据是动态的。 即使预定义区域性也是如此。 例如,国家或地区采用了新货币、更改了字词的拼写或其首选日历,区域性定义将更改以跟踪这一点。 自定义区域性可能会更改而不通知,并且任何特定区域性都可能被自定义替换区域性覆盖。 此外,如下所述,单个用户可以替代区域性首选项。 应用程序应始终在运行时获取区域性数据。

注意

保存数据时,应用程序应使用固定区域性、二进制格式或与区域性无关的特定格式。 根据与特定区域性(而非固定区域性)关联的当前值保存的数据可能会变得不可读,或者如果区域性发生更改,其含义可能会改变。

当前区域性和当前 UI 区域性

.NET 应用程序中的每个线程都具有当前区域性和当前 UI 区域性。 当前区域性确定日期、时间、数字和货币值的格式设置约定、文本排序顺序、大小写约定以及字符串的比较方式。 当前 UI 区域性用于在运行时检索区域性特定的资源。

注意

有关如何按线程确定当前区域性和当前 UI 区域性的详细信息,请参阅“区域性和线程”部分。 有关如何在新应用程序域中执行的线程以及跨应用程序域边界的线程上确定当前区域性和当前 UI 区域性的详细信息,请参阅“区域性和应用程序域”部分。 有关如何在执行基于任务的异步操作的线程上确定当前区域性和当前 UI 区域性的详细信息,请参阅“区域性和基于任务的异步操作”部分。

有关当前区域性的详细信息,请参阅 CultureInfo.CurrentCulture 属性。 有关当前 UI 区域性的详细信息,请参阅 CultureInfo.CurrentUICulture 属性主题。

检索当前区域性和当前 UI 区域性

可以通过以下两种方式之一获取表示当前区域性的 CultureInfo 对象:

以下示例检索了这两个属性值,对其进行比较以显示它们是否相等,并显示了当前区域性的名称。

using System;
using System.Globalization;
using System.Threading;

public class CurrentCultureEx
{
    public static void Main()
    {
        CultureInfo culture1 = CultureInfo.CurrentCulture;
        CultureInfo culture2 = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine("The current culture is {0}", culture1.Name);
        Console.WriteLine("The two CultureInfo objects are equal: {0}",
                          culture1 == culture2);
    }
}
// The example displays output like the following:
//     The current culture is en-US
//     The two CultureInfo objects are equal: True

可以通过以下两种方式之一获取表示当前 UI 区域性的 CultureInfo 对象:

以下示例检索了这两个属性值,对其进行比较以显示它们是否相等,并显示了当前 UI 区域性的名称。

using System;
using System.Globalization;
using System.Threading;

public class CurrentUIEx
{
    public static void Main()
    {
        CultureInfo uiCulture1 = CultureInfo.CurrentUICulture;
        CultureInfo uiCulture2 = Thread.CurrentThread.CurrentUICulture;
        Console.WriteLine("The current UI culture is {0}", uiCulture1.Name);
        Console.WriteLine("The two CultureInfo objects are equal: {0}",
                          uiCulture1 == uiCulture2);
    }
}
// The example displays output like the following:
//     The current UI culture is en-US
//     The two CultureInfo objects are equal: True

设置当前区域性和当前 UI 区域性

若要更改线程的区域性和 UI 区域性,请执行以下操作:

  1. 通过调用 CultureInfo 类构造函数并向其传递区域性的名称,实例化表示该区域性的 CultureInfo 对象。 如果新区域性与当前 Windows 区域性相同,CultureInfo(String) 构造函数将实例化反映用户替代的 CultureInfo 对象。 可以通过 CultureInfo(String, Boolean) 构造函数指定如果新区域性与当前 Windows 区域性相同,新实例化的 CultureInfo 对象是否反映用户替代。

  2. CultureInfo 对象分配给 .NET Core 和 .NET Framework 4.6 及更高版本上的 CultureInfo.CurrentCultureCultureInfo.CurrentUICulture 属性。

以下示例检索当前区域性。 如果是法语(法国)以外的区域性,则会将当前区域性更改为法语(法国)。 否则,它会将当前区域性更改为法国(卢森堡)。

using System;
using System.Globalization;

public class ChangeEx1
{
    public static void Main()
    {
        CultureInfo current = CultureInfo.CurrentCulture;
        Console.WriteLine("The current culture is {0}", current.Name);
        CultureInfo newCulture;
        if (current.Name.Equals("fr-FR"))
            newCulture = new CultureInfo("fr-LU");
        else
            newCulture = new CultureInfo("fr-FR");

        CultureInfo.CurrentCulture = newCulture;
        Console.WriteLine("The current culture is now {0}",
                          CultureInfo.CurrentCulture.Name);
    }
}
// The example displays output like the following:
//     The current culture is en-US
//     The current culture is now fr-FR

以下示例检索当前区域性。 如果是斯洛文尼亚语(斯洛文尼亚)以外的区域性,则会将目前的区域性更改为斯洛文尼亚语(斯洛文尼亚)。 否则,它会将目前的区域性更改为克罗地亚语(克罗地亚)。

using System;
using System.Globalization;

public class ChangeUICultureEx
{
    public static void Main()
    {
        CultureInfo current = CultureInfo.CurrentUICulture;
        Console.WriteLine("The current UI culture is {0}", current.Name);
        CultureInfo newUICulture;
        if (current.Name.Equals("sl-SI"))
            newUICulture = new CultureInfo("hr-HR");
        else
            newUICulture = new CultureInfo("sl-SI");

        CultureInfo.CurrentUICulture = newUICulture;
        Console.WriteLine("The current UI culture is now {0}",
                          CultureInfo.CurrentUICulture.Name);
    }
}
// The example displays output like the following:
//     The current UI culture is en-US
//     The current UI culture is now sl-SI

获取所有区域性

可以通过调用 GetCultures 该方法来检索大量特定类别的区域性或本地计算机上可用的所有区域性。 例如,可以单独或组合检索自定义区域性、特定区域性或非特定区域性。

以下示例调用了两次 GetCultures 方法,首先使用 System.Globalization.CultureTypes 枚举成员检索所有自定义区域性,然后使用 System.Globalization.CultureTypes 枚举成员检索所有替换区域性。

using System;
using System.Globalization;

public class GetCulturesEx
{
    public static void Main()
    {
        // Get all custom cultures.
        CultureInfo[] custom = CultureInfo.GetCultures(CultureTypes.UserCustomCulture);
        if (custom.Length == 0)
        {
            Console.WriteLine("There are no user-defined custom cultures.");
        }
        else
        {
            Console.WriteLine("Custom cultures:");
            foreach (var culture in custom)
                Console.WriteLine("   {0} -- {1}", culture.Name, culture.DisplayName);
        }
        Console.WriteLine();

        // Get all replacement cultures.
        CultureInfo[] replacements = CultureInfo.GetCultures(CultureTypes.ReplacementCultures);
        if (replacements.Length == 0)
        {
            Console.WriteLine("There are no replacement cultures.");
        }
        else
        {
            Console.WriteLine("Replacement cultures:");
            foreach (var culture in replacements)
                Console.WriteLine("   {0} -- {1}", culture.Name, culture.DisplayName);
        }
        Console.WriteLine();
    }
}
// The example displays output like the following:
//     Custom cultures:
//        x-en-US-sample -- English (United States)
//        fj-FJ -- Boumaa Fijian (Viti)
//
//     There are no replacement cultures.

区域性和线程

启动新的应用程序线程时,其当前区域性和当前 UI 区域性由当前系统区域性定义,而不是由当前线程区域性定义。 以下示例演示了差异。 它将应用程序线程的当前区域性和当前 UI 区域性设置为法语(法国)区域性 (fr-FR)。 如果当前区域性已经是 fr-FR,则示例将其设置为英语(美国)区域性 (en-US)。 它将三个随机数显示为货币值,然后创建一个新线程,再将另外三个随机数显示为货币值。 但是,如示例中的输出所示,新线程显示的货币值并不反映法语(法国)区域性的格式设置约定,这与主应用程序线程的输出不同。

using System;
using System.Globalization;
using System.Threading;

public class DefaultThreadEx
{
    static Random rnd = new Random();

    public static void Main()
    {
        if (Thread.CurrentThread.CurrentCulture.Name != "fr-FR")
        {
            // If current culture is not fr-FR, set culture to fr-FR.
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("fr-FR");
        }
        else
        {
            // Set culture to en-US.
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("en-US");
        }
        ThreadProc();

        Thread worker = new Thread(ThreadProc);
        worker.Name = "WorkerThread";
        worker.Start();
    }

    private static void DisplayThreadInfo()
    {
        Console.WriteLine("\nCurrent Thread Name: '{0}'",
                          Thread.CurrentThread.Name);
        Console.WriteLine("Current Thread Culture/UI Culture: {0}/{1}",
                          Thread.CurrentThread.CurrentCulture.Name,
                          Thread.CurrentThread.CurrentUICulture.Name);
    }

    private static void DisplayValues()
    {
        // Create new thread and display three random numbers.
        Console.WriteLine("Some currency values:");
        for (int ctr = 0; ctr <= 3; ctr++)
            Console.WriteLine("   {0:C2}", rnd.NextDouble() * 10);
    }

    private static void ThreadProc()
    {
        DisplayThreadInfo();
        DisplayValues();
    }
}
// The example displays output similar to the following:
//       Current Thread Name: ''
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          8,11 €
//          1,48 €
//          8,99 €
//          9,04 €
//
//       Current Thread Name: 'WorkerThread'
//       Current Thread Culture/UI Culture: en-US/en-US
//       Some currency values:
//          $6.72
//          $6.35
//          $2.90
//          $7.72

可以通过将表示该区域性的 CultureInfo 对象分配给 DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 属性来设置应用程序域中所有线程的区域性和 UI 区域性。 以下示例使用这些属性来确保默认应用程序域中的所有线程的区域性相同。

using System;
using System.Globalization;
using System.Threading;

public class SetThreadsEx
{
    static Random rnd = new Random();

    public static void Main()
    {
        if (Thread.CurrentThread.CurrentCulture.Name != "fr-FR")
        {
            // If current culture is not fr-FR, set culture to fr-FR.
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture("fr-FR");
        }
        else
        {
            // Set culture to en-US.
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture("en-US");
        }
        ThreadProc();

        Thread worker = new Thread(SetThreadsEx.ThreadProc);
        worker.Name = "WorkerThread";
        worker.Start();
    }

    private static void DisplayThreadInfo()
    {
        Console.WriteLine("\nCurrent Thread Name: '{0}'",
                          Thread.CurrentThread.Name);
        Console.WriteLine("Current Thread Culture/UI Culture: {0}/{1}",
                          Thread.CurrentThread.CurrentCulture.Name,
                          Thread.CurrentThread.CurrentUICulture.Name);
    }

    private static void DisplayValues()
    {
        // Create new thread and display three random numbers.
        Console.WriteLine("Some currency values:");
        for (int ctr = 0; ctr <= 3; ctr++)
            Console.WriteLine("   {0:C2}", rnd.NextDouble() * 10);
    }

    private static void ThreadProc()
    {
        DisplayThreadInfo();
        DisplayValues();
    }
}
// The example displays output similar to the following:
//       Current Thread Name: ''
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          6,83 €
//          3,47 €
//          6,07 €
//          1,70 €
//
//       Current Thread Name: 'WorkerThread'
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          9,54 €
//          9,50 €
//          0,58 €
//          6,91 €

警告

虽然 DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 属性是静态成员,但它们仅为设置这些属性值时的当前应用程序域定义默认区域性和默认 UI 区域性。 有关详细信息,请参阅下一部分:区域性和应用程序域

将值分配给 DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 属性时,如果应用程序域中线程的区域性和 UI 区域性尚未显式分配给区域性,则它们也会更改。 但是,这些线程仅在当前应用程序域中执行时反映新的区域性设置。 如果这些线程在另一个应用程序域中执行,则其区域性将成为为该应用程序域定义的默认区域性。 因此,建议始终设置主应用程序线程的区域性,而不依赖于 DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 属性来更改它。

区域性和应用程序域

DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 是静态属性,仅针对设置或检索属性值时的当前应用程序域显式定义默认区域性。 以下示例将默认应用程序域中的默认区域性和默认 UI 区域性设置为法语(法国),然后使用 AppDomainSetup 类和 AppDomainInitializer 委托在新应用程序域中将默认区域性和 UI 区域性设置为俄语(俄罗斯)。 然后,单个线程在每个应用程序域中执行两种方法。 请注意,线程的区域性和 UI 区域性未显式设置;它们派生自执行线程的应用程序域的默认区域性和 UI 区域性。 另请注意,DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 属性返回方法调用时的当前应用程序域的默认 CultureInfo 值。

using System;
using System.Globalization;

public class Example
{
    public static void Main()
    {
        // Set the default culture and display the current date in the current application domain.
        Info info1 = new Info();
        SetAppDomainCultures("fr-FR");

        // Create a second application domain.
        AppDomainSetup setup = new AppDomainSetup();
        setup.AppDomainInitializer = SetAppDomainCultures;
        setup.AppDomainInitializerArguments = new string[] { "ru-RU" };
        AppDomain domain = AppDomain.CreateDomain("Domain2", null, setup);
        // Create an Info object in the new application domain.
        Info info2 = (Info)domain.CreateInstanceAndUnwrap(typeof(Example).Assembly.FullName,
                                                           "Info");

        // Execute methods in the two application domains.
        info2.DisplayDate();
        info2.DisplayCultures();

        info1.DisplayDate();
        info1.DisplayCultures();
    }

    public static void SetAppDomainCultures(string[] names)
    {
        SetAppDomainCultures(names[0]);
    }

    public static void SetAppDomainCultures(string name)
    {
        try
        {
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(name);
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture(name);
        }
        // If an exception occurs, we'll just fall back to the system default.
        catch (CultureNotFoundException)
        {
            return;
        }
        catch (ArgumentException)
        {
            return;
        }
    }
}

public class Info : MarshalByRefObject
{
    public void DisplayDate()
    {
        Console.WriteLine("Today is {0:D}", DateTime.Now);
    }

    public void DisplayCultures()
    {
        Console.WriteLine("Application domain is {0}", AppDomain.CurrentDomain.Id);
        Console.WriteLine("Default Culture: {0}", CultureInfo.DefaultThreadCurrentCulture);
        Console.WriteLine("Default UI Culture: {0}", CultureInfo.DefaultThreadCurrentUICulture);
    }
}
// The example displays the following output:
//       Today is 14 октября 2011 г.
//       Application domain is 2
//       Default Culture: ru-RU
//       Default UI Culture: ru-RU
//       Today is vendredi 14 octobre 2011
//       Application domain is 1
//       Default Culture: fr-FR
//       Default UI Culture: fr-FR

有关区域性和应用程序域的详细信息,请参阅应用程序域主题中的“应用程序域和线程”部分。

区域性和基于任务的异步操作

基于任务的异步编程模式使用 TaskTask<TResult> 对象在线程池线程上异步执行委托。 无法事先知道特定任务在哪个线程运行,只有在运行时才能确定。

对于面向 .NET Framework 4.6 或更高版本的应用,区域性是异步操作上下文的一部分。 换句话说,异步操作默认继承启动该操作的线程的 CurrentCultureCurrentUICulture 属性的值。 如果当前区域性或当前 UI 区域性与系统区域性不同,则当前区域性会跨越线程边界,并成为正在执行异步操作的线程池线程的当前区域性。

下面的示例提供了简单的演示。 该示例定义一个 Func<TResult> 委托,formatDelegate,该委托返回一些格式设置为货币值的数字。 本示例将当前系统区域性更改为法语(法国),或者如果法语(法国)已是当前区域性,则改为英语(美国)。 然后它:

  • 直接调用委托,以便它在主应用线程上同步运行。
  • 创建在线程池线程上异步执行委托的任务。
  • 通过调用 Task.RunSynchronously 方法,创建在主应用线程上同步执行委托的任务。

如示例中的输出所示,当前区域性更改为法语(法国)时,从中异步调用任务的线程的当前区域性将成为该异步操作的当前区域性。

using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

public class AsyncCultureEx1
{
    public static void Main()
    {
        decimal[] values = { 163025412.32m, 18905365.59m };
        string formatString = "C2";

        string FormatDelegate()
        {
            string output = $"Formatting using the {CultureInfo.CurrentCulture.Name} " +
            "culture on thread {Thread.CurrentThread.ManagedThreadId}.\n";
            foreach (decimal value in values)
                output += $"{value.ToString(formatString)}   ";

            output += Environment.NewLine;
            return output;
        }

        Console.WriteLine($"The example is running on thread {Thread.CurrentThread.ManagedThreadId}");
        // Make the current culture different from the system culture.
        Console.WriteLine($"The current culture is {CultureInfo.CurrentCulture.Name}");
        if (CultureInfo.CurrentCulture.Name == "fr-FR")
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        else
            Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

        Console.WriteLine($"Changed the current culture to {CultureInfo.CurrentCulture.Name}.\n");

        // Execute the delegate synchronously.
        Console.WriteLine("Executing the delegate synchronously:");
        Console.WriteLine(FormatDelegate());

        // Call an async delegate to format the values using one format string.
        Console.WriteLine("Executing a task asynchronously:");
        var t1 = Task.Run(FormatDelegate);
        Console.WriteLine(t1.Result);

        Console.WriteLine("Executing a task synchronously:");
        var t2 = new Task<string>(FormatDelegate);
        t2.RunSynchronously();
        Console.WriteLine(t2.Result);
    }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €

DefaultThreadCurrentCultureDefaultThreadCurrentUICulture 是每应用域属性;也就是说,它们为所有未在特定应用程序域中显式分配区域性的线程建立默认区域性。 但是,对于面向 .NET Framework 4.6 或更高版本的应用,调用线程的区域性仍然是异步任务上下文的一部分,即使任务跨越应用域边界也是如此。

CultureInfo 对象序列化

序列化 CultureInfo 对象时,实际存储的所有对象都是 NameUseUserOverride。 它仅会在 Name 具有相同含义的环境中成功反序列化。 以下三个示例演示了为何情况并非总是如此:

  • 如果 CultureTypes 属性值为 CultureTypes.InstalledWin32Cultures,并且该区域性首次在 Windows 操作系统的特定版本中引入,则无法在早期版本的 Windows 上反序列化它。 例如,如果一个区域性在 Windows 10 中引入,则无法在 Windows 8 上反序列化它。

  • 如果 CultureTypes 值为 CultureTypes.UserCustomCulture,并且反序列化它的计算机未安装此用户自定义区域性,则无法反序列化它。

  • 如果 CultureTypes 值为 CultureTypes.ReplacementCultures,并且反序列化它的计算机没有此替换区域性,则它将反序列化为同一名称,但并非所有特征都相同。 例如,如果 en-US 是计算机 A 上的替换区域性,但不是计算机 B 上的替换区域性,并且引用此区域性的 CultureInfo 对象在计算机 A 上序列化,并在计算机 B 上反序列化,则不会传输区域性的任何自定义特征。 区域性已成功反序列化,但含义不同。

控制面板替代

用户可能会选择通过控制面板的区域和语言选项替代与 Windows 当前区域性相关的一些值。 例如,用户可能选择以不同的格式显示日期,或使用区域性默认值以外的货币。 通常,应用程序应遵循这些用户替代。

如果 UseUserOverridetrue 且指定区域性与 Windows 的当前区域性匹配,则 CultureInfo 使用这些替代,包括 DateTimeFormat 属性返回的 DateTimeFormatInfo 实例的属性的用户设置,以及 NumberFormat 属性返回的 NumberFormatInfo 实例的属性。 如果用户设置与 CultureInfo 的关联区域性不兼容(例如,如果所选日历不是 OptionalCalendars 其中之一),则方法的结果和属性的值未定义。

替换排序顺序

某些区域性支持多个排序顺序。 例如:

  • 西班牙语(西班牙)区域性有两种排序顺序:默认的国际排序顺序和传统排序顺序。 使用 es-ES 区域性名称实例化 CultureInfo 对象时,将使用国际排序顺序。 使用 es-ES-tradnl 区域性名称实例化 CultureInfo 对象时,将使用传统排序顺序。

  • zh-CN(简体中文,中国)区域性支持两种排序顺序:按发音排序(默认)和按笔画数排序。 使用 zh-CN 区域性名称实例化 CultureInfo 对象时,将使用默认排序顺序。 实例化具有本地标识符 0x00020804 的 CultureInfo 对象时,字符串按笔画数排序。

下表列出了支持替换排序顺序的区域性以及默认排序顺序和替换排序顺序的标识符。

区域性名称 区域性 默认排序名称和标识符 替换排序名称和标识符
es-ES 西班牙语(西班牙) 国际:0x00000C0A 传统:0x0000040A
zh-TW 中文(台湾) 笔画数:0x00000404 注音符号: 0x00030404
zh-CN 中文(中华人民共和国) 发音:0x00000804 笔画数:0x00020804
zh-HK 中文(香港特别行政区) 笔画数:0x00000c04 笔画数:0x00020c04
Zh-SG 中文(新加坡) 发音:0x00001004 笔画数:0x00021004
zh-MO 中文(澳门特别行政区) 发音:0x00001404 笔画数:0x00021404
ja-JP 日语(日本) 默认:0x00000411 Unicode:0x00010411
ko-KR 韩语(韩国) 默认:0x00000412 朝鲜语 Xwansung - Unicode:0x00010412
de-DE 德语(德国) 字典:0x00000407 电话簿排序 DIN:0x00010407
hu-HU 匈牙利语(匈牙利) 默认:0x0000040e 技术排序:0x0001040e
ka-GE 格鲁吉亚语(格鲁吉亚) 传统:0x00000437 现代排序:0x00010437

当前区域性和 UWP 应用

和在 .NET Framework 和 .NET Core 应用中一样,CurrentCultureCurrentUICulture 属性在通用 Windows 平台 (UWP) 应用中是可读写的。 但是,UWP 应用可识别单个区域性。 CurrentCultureCurrentUICulture 属性映射到 Windows.ApplicationModel.Resources.Core.ResourceManager.DefaultContext.Languages 集合中的第一个值。

在 .NET 应用中,当前区域性是每线程设置,并且 CurrentCultureCurrentUICulture 属性仅反映当前线程的区域性和 UI 区域性。 在 UWP 应用中,当前区域性映射到 Windows.ApplicationModel.Resources.Core.ResourceManager.DefaultContext.Languages 集合,这是一个全局设置。 设置 CurrentCultureCurrentUICulture 属性会更改整个应用的区域性;区域性不能按线程设置。