在不从 LCID 创建 RegionInfo 的情况下比较文化区域
Compare a Cultures region without creating a RegionInfo from LCID
为了列出给定区域的适用文化,这是我当前的代码:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => Equals(new RegionInfo(c.LCID), regionInfo));
}
但我不喜欢在 Where
中创建 RegionInfo
。
我真的很想做这件事:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => c.LCID == regionInfo.LCID);
}
但是 RegionInfo
上没有 LCID
,所以我不能。除了更好的解决方案之外,了解为什么 RegionInfo
没有 LCID
.
会很有趣
示例测试:
[TestMethod]
public void Can_find_all_by_region_ES_valencia()
{
var regionInfo = new RegionInfo(new CultureInfo("ca-ES-valencia").LCID);
List<CultureInfo> found = ForRegion(regionInfo).ToList();
Assert.AreEqual(5, found.Count);
CollectionAssert.AreEqual(
new[]
{
new CultureInfo("ca-ES"),
new CultureInfo("ca-ES-valencia"), //missing with Jon's solution
new CultureInfo("es-ES"),
new CultureInfo("eu-ES"),
new CultureInfo("gl-ES")
},
found);
}
这适用于我当前的解决方案,但不适用于 Jon 的解决方案。
另外两个测试,乔恩修改后的解决方案失败(适用于原始解决方案):
[TestMethod]
public void Can_find_all_by_region_Germany()
{
List<CultureInfo> found = ForRegion(new RegionInfo("DE")).ToList();
Assert.AreEqual(3, found.Count);
CollectionAssert.AreEqual(
new[] {new CultureInfo("de-DE"), new CultureInfo("dsb-DE"), new CultureInfo("hsb-DE")}, found);
}
[TestMethod]
public void Round_trip_all_cultures()
{
foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
Assert.IsTrue(ForRegion(new RegionInfo(culture.LCID)).Contains(culture), culture.Name);
}
从您从查询中获得的结果来看,这应该非常明显 - 同一区域可以有多个区域设置(因此,LCID)。因此,充其量,您可以获得给定区域的 LCID 列表 - 如果您想维护该列表,只需自己制作即可。
例如,地区 GB
(英国)拥有三个不同的语言区域 - cy-GB
(1106)、gd-GB
(1169) 和 en-GB
(2057 ).
就性能而言,嗯...除非您可以避免在您的 Where
中分配 所有,否则没有什么可调整的。即使是简单的 string.Split
也可能比简单地 new RegionInfo
花费更多。文化数据被缓存,所以在第一次之后只有与这种操作相关的内存成本。当然,如果您不介意内存成本,也没有什么可以阻止您自己构建查找。
正如 Jon Skeet 指出的那样,RegionInfo
与 Name
进行了比较。当您从 LCID
创建 RegionInfo
时,名称实际上来自 CultureData
上的 属性 它已被 LCID
.
找到
所以我添加了一个扩展方法,可以有效地(至少出于我的目的,即在我的解决方案中我需要创建每个区域一次)从文化中获取区域名称。
public static class CultureInfoExtension
{
private static readonly Dictionary<int, string> RegionNamesByLcid =
CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Select(c => c.LCID)
.Distinct()
.ToDictionary(lcid => lcid, lcid => new RegionInfo(lcid).Name);
private static string FallbackToThrowConsistentException(CultureInfo c)
{
return new RegionInfo(c.LCID).Name;
}
public static string GetRegionName(this CultureInfo c)
{
string name;
return RegionNamesByLcid.TryGetValue(c.LCID, out name)
? name
: FallbackToThrowConsistentException(c);
}
}
用法:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => CultureBelongsToRegion(c, regionInfo));
}
private static bool CultureBelongsToRegion(CultureInfo c, RegionInfo regionInfo)
{
return c.GetRegionName() == regionInfo.Name;
}
}
为了列出给定区域的适用文化,这是我当前的代码:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => Equals(new RegionInfo(c.LCID), regionInfo));
}
但我不喜欢在 Where
中创建 RegionInfo
。
我真的很想做这件事:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => c.LCID == regionInfo.LCID);
}
但是 RegionInfo
上没有 LCID
,所以我不能。除了更好的解决方案之外,了解为什么 RegionInfo
没有 LCID
.
示例测试:
[TestMethod]
public void Can_find_all_by_region_ES_valencia()
{
var regionInfo = new RegionInfo(new CultureInfo("ca-ES-valencia").LCID);
List<CultureInfo> found = ForRegion(regionInfo).ToList();
Assert.AreEqual(5, found.Count);
CollectionAssert.AreEqual(
new[]
{
new CultureInfo("ca-ES"),
new CultureInfo("ca-ES-valencia"), //missing with Jon's solution
new CultureInfo("es-ES"),
new CultureInfo("eu-ES"),
new CultureInfo("gl-ES")
},
found);
}
这适用于我当前的解决方案,但不适用于 Jon 的解决方案。
另外两个测试,乔恩修改后的解决方案失败(适用于原始解决方案):
[TestMethod]
public void Can_find_all_by_region_Germany()
{
List<CultureInfo> found = ForRegion(new RegionInfo("DE")).ToList();
Assert.AreEqual(3, found.Count);
CollectionAssert.AreEqual(
new[] {new CultureInfo("de-DE"), new CultureInfo("dsb-DE"), new CultureInfo("hsb-DE")}, found);
}
[TestMethod]
public void Round_trip_all_cultures()
{
foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
Assert.IsTrue(ForRegion(new RegionInfo(culture.LCID)).Contains(culture), culture.Name);
}
从您从查询中获得的结果来看,这应该非常明显 - 同一区域可以有多个区域设置(因此,LCID)。因此,充其量,您可以获得给定区域的 LCID 列表 - 如果您想维护该列表,只需自己制作即可。
例如,地区 GB
(英国)拥有三个不同的语言区域 - cy-GB
(1106)、gd-GB
(1169) 和 en-GB
(2057 ).
就性能而言,嗯...除非您可以避免在您的 Where
中分配 所有,否则没有什么可调整的。即使是简单的 string.Split
也可能比简单地 new RegionInfo
花费更多。文化数据被缓存,所以在第一次之后只有与这种操作相关的内存成本。当然,如果您不介意内存成本,也没有什么可以阻止您自己构建查找。
正如 Jon Skeet 指出的那样,RegionInfo
与 Name
进行了比较。当您从 LCID
创建 RegionInfo
时,名称实际上来自 CultureData
上的 属性 它已被 LCID
.
所以我添加了一个扩展方法,可以有效地(至少出于我的目的,即在我的解决方案中我需要创建每个区域一次)从文化中获取区域名称。
public static class CultureInfoExtension
{
private static readonly Dictionary<int, string> RegionNamesByLcid =
CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Select(c => c.LCID)
.Distinct()
.ToDictionary(lcid => lcid, lcid => new RegionInfo(lcid).Name);
private static string FallbackToThrowConsistentException(CultureInfo c)
{
return new RegionInfo(c.LCID).Name;
}
public static string GetRegionName(this CultureInfo c)
{
string name;
return RegionNamesByLcid.TryGetValue(c.LCID, out name)
? name
: FallbackToThrowConsistentException(c);
}
}
用法:
public IEnumerable<CultureInfo> ForRegion(RegionInfo regionInfo)
{
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Where(c => CultureBelongsToRegion(c, regionInfo));
}
private static bool CultureBelongsToRegion(CultureInfo c, RegionInfo regionInfo)
{
return c.GetRegionName() == regionInfo.Name;
}
}