C# 这是临时更改当前线程的好方法吗';中国文化?
我在一个相当大的ASP.NETWebForms应用程序上工作,该应用程序目前主要在美国使用。我们正在将其推广到世界其他地区,这当然意味着我们目前正在致力于将应用程序的所有领域本地化。一般来说,我们的方法是在每个请求开始时设置当前线程的CurrentCulture和CurrentUICulture属性,以支持基于当前用户的区域设置的正确格式和资源提取 但是,在某些情况下,我们需要使用当前用户的区域性以外的区域性运行特定的代码位。例如,“用户A”居住在德国,但为一家与法国其他公司开展业务的公司工作。当“用户A”希望为其中一家法国公司创建发票(PDF)时,我们希望发票生成代码使用“fr fr”区域性而不是“de de”区域性运行 我已经考虑了几种简单的方法,我想知道我是否正确地进行了这项工作。我主要关心的是性能和线程安全 一种方法涉及一个静态方法,该方法旨在使用提供的区域性运行给定的任务。大概是这样的:C# 这是临时更改当前线程的好方法吗';中国文化?,c#,.net,localization,cultureinfo,C#,.net,Localization,Cultureinfo,我在一个相当大的ASP.NETWebForms应用程序上工作,该应用程序目前主要在美国使用。我们正在将其推广到世界其他地区,这当然意味着我们目前正在致力于将应用程序的所有领域本地化。一般来说,我们的方法是在每个请求开始时设置当前线程的CurrentCulture和CurrentUICulture属性,以支持基于当前用户的区域设置的正确格式和资源提取 但是,在某些情况下,我们需要使用当前用户的区域性以外的区域性运行特定的代码位。例如,“用户A”居住在德国,但为一家与法国其他公司开展业务的公司工作。
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));
var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))
{
invoiceService.CreateInvoice(currentCustomer.CustomerId);
}
public static class CultureInfoExtensions {
public static IDisposable AsCurrent(this CultureInfo culture) {
return new CultureRunner(culture);
}
}
然后可以如下方式调用此方法:
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));
var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))
{
invoiceService.CreateInvoice(currentCustomer.CustomerId);
}
public static class CultureInfoExtensions {
public static IDisposable AsCurrent(this CultureInfo culture) {
return new CultureRunner(culture);
}
}
我还考虑过创建一个实现IDisposable的类,该类将负责在其ctor中设置线程区域性,然后在Dispose方法中返回原始区域性,因此您可以这样调用它:
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));
var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))
{
invoiceService.CreateInvoice(currentCustomer.CustomerId);
}
public static class CultureInfoExtensions {
public static IDisposable AsCurrent(this CultureInfo culture) {
return new CultureRunner(culture);
}
}
我这样做完全错了吗?如果这些方法中有哪一种更可取?我喜欢使用
方法。我还将创建一个扩展方法,以提高阅读效果:
var customerCulture = new CultureInfo(currentCustomer.Locale);
using (customerCulture.AsCurrent()) {
invoiceService.CreateInvoice(currentCustomer.CustomerId);
}
大概是这样的:
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));
var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))
{
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);
}
既然你在问暂时改变当前线程的文化是否是个好主意,我只能回答:否。如果并且只有在没有其他方法让事情运转的情况下,才可以使用它。这只是因为这种切换容易出错。好吧,你不会忘记用Jordão(尊敬)给你的代码把事情改回去,但是…
目前,您的客户希望创建法国发票。我假设您希望使用法语日期、数字和货币格式。没关系。但是如果将来某个将来需要以其他格式打印出来,例如,这是源于德语的格式,该怎么办?你打算创造一些丑陋的作品吗
我知道它可能超出您的控制范围(比如报告软件可能是第三方独立解决方案,您无法控制它如何处理ToString()
),但如果它在您的控制范围内,我建议首先以正确的格式提供数据。例如,您可以创建一些数据转换层(DTO)并正确格式化数据(通过ToString(IFormatProvider)
)。我知道这是一个相当大的努力,但既然你问的是正确的做事方式
如果我们在同一个组织中,我会进行I18n代码审查,您可以肯定,我会指出暂时性的文化变化是一种缺陷。通常有一种方法可以避免这种情况。使用方法对我来说也是最好的。谢谢您的回复。当你说“…这种切换容易出错”时,你的意思是它会导致难以维护的代码,还是这种方法会导致应用程序中的实际运行时错误?此外,我没有选择使用ToString(IFormatProvider)方法的原因之一是我需要的不仅仅是正确的日期和数字格式;我还需要从resx文件中提取一些正确语言的静态文本。除了更改当前线程上的CurretUICulture属性之外,我还没有找到一种有效且安全的方法来使用当前语言拉入消息。它不应该在应用程序的其他部分导致错误,至少在您记得将其切换回时是这样。至于从resx文件中读取,您可以始终实例化ResourceManager,并将GetString()与适当的CultureInfo一起使用。您可以添加更通用的示例,因为CultureRunner未声明。我需要使用不同的区域性运行一个方法。你能更新你的答案吗?