这是暂时改变当前线程文化的好方法吗?

Posted

技术标签:

【中文标题】这是暂时改变当前线程文化的好方法吗?【英文标题】:Is this a good approach for temporarily changing the current thread's culture? 【发布时间】:2011-08-13 04:11:48 【问题描述】:

我在一个相当大的 ASP .NET Web 窗体应用程序上工作,该应用程序目前主要在美国使用。我们正在将其推广到世界其他地区,这当然意味着我们目前正在努力本地化应用程序的所有领域。一般来说,我们的方法是在每个请求开始时设置当前线程的 CurrentCulture 和 CurrentUICulture 属性,以支持基于当前用户区域设置的正确格式和资源提取。

然而,在某些情况下,我们需要使用不同于当前用户的文化的文化来运行某段代码。例如,“用户 A”居住在德国,但在一家与法国其他公司开展业务的公司工作。当“用户 A”想要为其中一家法国公司创建发票 (PDF) 时,我们希望发票生成代码以“fr-FR”文化而不是“de-DE”文化运行。

我已经考虑了几种轻松完成此操作的方法,并且想知道我是否正确地进行了此操作。我主要关心的是性能和线程安全。

一种方法涉及一种静态方法,旨在以提供的文化运行给定任务。像这样的:

 public static void RunWithCulture(CultureInfo culture, Action task)
    
        if (culture == null)
            throw new ArgumentNullException("culture");

        var originalCulture = new
                                  
                                      Culture = Thread.CurrentThread.CurrentCulture,
                                      UICulture = Thread.CurrentThread.CurrentUICulture
                                  ;

        try
        
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
            task();
        
        finally
        
            Thread.CurrentThread.CurrentCulture = originalCulture.Culture;
            Thread.CurrentThread.CurrentUICulture = originalCulture.UICulture;
        
    

然后可以像这样调用此方法:

var customerCulture = new CultureInfo(currentCustomer.Locale);
CultureRunner.RunWithCulture(customerCulture, () => invoiceService.CreateInvoice(currentCustomer.CustomerId));

我还考虑创建一个实现 IDisposable 的类,该类负责在其 ctor 中设置线程文化,然后在 Dispose 方法中返回原始文化,因此您可以这样称呼它:

var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))

  invoiceService.CreateInvoice(currentCustomer.CustomerId);

我做错了吗?如果这些方法中的任何一种更可取?

【问题讨论】:

使用方法对我来说也是最好的。 【参考方案1】:

我喜欢using 方法。我还会创建一个扩展方法以使内容更好地阅读:

var customerCulture = new CultureInfo(currentCustomer.Locale);  
using (customerCulture.AsCurrent()) 
  invoiceService.CreateInvoice(currentCustomer.CustomerId);

类似这样的:

public static class CultureInfoExtensions 
  public static IDisposable AsCurrent(this CultureInfo culture) 
    return new CultureRunner(culture);
  

CultureRunner 示例:

public class CultureRunner : IDisposable

    readonly CultureInfo originalCulture;
    readonly CultureInfo originalUICulture;

    public CultureRunner(CultureInfo culture)
    
        if (culture == null)
            throw new ArgumentNullException(nameof(culture));

        originalCulture = Thread.CurrentThread.CurrentCulture;
        originalUICulture = Thread.CurrentThread.CurrentUICulture;

        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    

    public void Dispose()
    
        Thread.CurrentThread.CurrentCulture = originalCulture;
        Thread.CurrentThread.CurrentUICulture = originalUICulture;
    

或者,如果设置文化的始终是您的客户对象,那么另一种扩展方法将进一步提升抽象:

using (currentCustomer.CultureContext()) 
  invoiceService.CreateInvoice(currentCustomer.CustomerId);

【讨论】:

您可以添加更通用的示例,因为没有声明 CultureRunner。我需要使用不同的文化来运行一种方法。你能更新你的答案吗?【参考方案2】:

既然你问临时改变当前线程的文化是否是个好主意,我只能回答:。当且仅当没有其他方法可以使事情正常运行时,才可以使用它。那只是因为这种切换容易出错。好的,您不会忘记使用 Jordão(尊重)给您的代码更改内容,但是... 现在,您有客户想要创建法国发票。我假设您想使用法语日期、数字和货币格式。没关系。但是......如果将来某些未来需要以其他格式打印出来,例如这个原始德语怎么办?你打算创造一些丑陋的变通办法吗?

我了解它可能超出您的控制范围(例如报告软件可能是 3rd 方独立解决方案,您无法控制它如何处理 ToString())但如果它在您的控制,我建议首先以正确的格式提供数据。例如,您可以创建一些数据转换层 (DTO) 并正确格式化数据(通过ToString(IFormatProvider))。我知道这是一项相当大的努力,但既然你问的是正确的做事方式......

如果我们在同一个组织中并且我会进行 I18n 代码审查,您可以肯定我会指出临时改变文化是一个缺陷。通常有办法避免这种情况。

【讨论】:

感谢您的回复。当您说“...这种切换容易出错”时,您的意思是它会导致代码难以维护,还是这种方法会导致应用程序中出现实际的运行时错误? 另外,我没有选择使用 ToString(IFormatProvider) 方法的原因之一是我需要的不仅仅是正确的日期和数字格式;我还需要从 resx 文件中提取一些正确语言的静态文本。除了更改当前线程上的 CurretUICulture 属性之外,我还没有找到一种有效且安全的方法来使用当前语言提取消息。 容易出错的意思是维护。它不应该在应用程序的其他部分引起错误,至少当你记得将它切换回来时。 对于从 resx 文件中读取,您始终可以实例化 ResourceManager 并将 GetString() 与适当的 CultureInfo 一起使用。

以上是关于这是暂时改变当前线程文化的好方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin.mac 资源本地化不会因当前文化而改变

我可以获得当前线程产生的类和方法的名称吗?

AtomicInteger 是为多线程应用程序提供计数器的好解决方案吗?

这是确定操作系统架构的好方法吗?

改变当前文化

多线程调用同一个方法,局部变量会共享吗