在 .net 中本地化枚举描述的最佳方法是什么?
(有关枚举描述示例,请参阅向枚举常量添加描述)
理想情况下,我想要使用 ResourceManager 和资源文件的东西,以便它适合应用程序其他区域的本地化方式。
这就是我最终的结果,我没有看到添加自定义属性类来保存资源键然后查找资源文件的价值 - 为什么不只使用枚举类型名+值作为资源键?
using System;
using System.Resources;
using System.Reflection;
public class MyClass
{
enum SomeEnum {Small,Large};
private ResourceManager _resources = new ResourceManager("MyClass.myResources",
System.Reflection.Assembly.GetExecutingAssembly());
public string EnumDescription(Enum enumerator)
{
string rk = String.Format("{0}.{1}",enumerator.GetType(),enumerator);
string localizedDescription = _resources.GetString(rk);
if (localizedDescription == null)
{
// A localized string was not found so you can either just return
// the enums value - most likely readable and a good fallback.
return enumerator.ToString();
// Or you can return the full resourceKey which will be helpful when
// editing the resource files(e.g. MyClass+SomeEnum.Small)
// return resourceKey;
}
else
return localizedDescription;
}
void SomeRoutine()
{
// Looks in resource file for a string matching the key
// "MyClass+SomeEnum.Large"
string s1 = EnumDescription(SomeEnum.Large);
}
}
我的解决方案,使用本机描述属性:
public class LocalizedEnumAttribute : DescriptionAttribute
{
private PropertyInfo _nameProperty;
private Type _resourceType;
public LocalizedEnumAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public Type NameResourceType
{
get
{
return _resourceType;
}
set
{
_resourceType = value;
_nameProperty = _resourceType.GetProperty(this.Description, BindingFlags.Static | BindingFlags.Public);
}
}
public override string Description
{
get
{
//check if nameProperty is null and return original display name value
if (_nameProperty == null)
{
return base.Description;
}
return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
}
}
}
public static class EnumExtender
{
public static string GetLocalizedDescription(this Enum @enum)
{
if (@enum == null)
return null;
string description = @enum.ToString();
FieldInfo fieldInfo = @enum.GetType().GetField(description);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return attributes[0].Description;
return description;
}
}
枚举声明
public enum MyEnum
{
[LocalizedEnum("ResourceName", NameResourceType = typeof(ResourceType))]
Test = 0
}
然后致电
MyEnumInstance.GetLocalizedDescription()
有一个简单的解决方案: 使用 LocalizedDescription 属性来传递资源密钥。
[Serializable]
public class LocalizableDescriptionAttribute:DescriptionAttribute
{
public LocalizableDescriptionAttribute(string resourceKey)
:base(Resources.ResourceManager.GetString(resourceKey))
{ }
}
我曾经做过的一种方法是在与枚举相同的命名空间中添加扩展方法,该方法返回一个字符串。就我而言,它只是硬编码,但从资源文件中获取它们是没有问题的。
public static string Describe(this SomeEnum e)
{
switch(e)
{
SomeEnum.A:
return "Some text from resourcefile";
SomeEnum.B:
return "Some other text from resourcefile";
...:
return ...;
}
}
也许不是一个极其流畅或奇特的解决方案,但它有效 =)
将 @nairik 的方法替换为以下内容以添加对标志枚举的支持。
public static string GetLocalizedDescription(this Enum @enum)
{
if ( @enum == null )
return null;
StringBuilder sbRet = new StringBuilder();
string description = @enum.ToString();
var fields = description.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach ( var field in fields )
{
FieldInfo fieldInfo = @enum.GetType().GetField(field);
DescriptionAttribute[] attributes = ( DescriptionAttribute[] )fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if ( attributes.Any() )
sbRet.AppendFormat("{0}, ", attributes[0].Description);
else
sbRet.AppendFormat("{0}, ", field);
}
if ( sbRet.Length > 2 )
sbRet.Remove(sbRet.Length - 2, 2);
return sbRet.ToString();
}
并替换属性中的NameResourceType:
public Type NameResourceType
{
get
{
return _resourceType;
}
set
{
_resourceType = value;
_nameProperty = _resourceType.GetProperty(base.Description, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
}
}
请参阅我在这个问题中的表格示例:
状态类型表映射到枚举值。这里真正的好处是,您可以在报告和跨应用程序中进行本地化,并指定外部 ID 以便与不需要您的内部值等的第三方集成。它将枚举描述与其值分离。
您不能应用多个 System.ComponentModel.DescriptionAttribute(因此该选项已退出)。
因此添加一个间接级别,描述保存资源名称,然后使用资源中的本地化支持。显然,枚举的用户需要调用您的辅助方法来执行此操作。
我的解决方案受到nairik的答案的启发,是使用已经存在的
DisplayAttribute
:
using System.ComponentModel.DataAnnotations;
using System.Reflection;
public static string GetLocalizedDescription(this Enum enumValue)
{
string description = enumValue.ToString();
FieldInfo fieldInfo = enumValue.GetType().GetField(description)!;
return fieldInfo.GetCustomAttribute<DisplayAttribute>(false)?.GetName() ?? description;
}
属性使用:
using System.ComponentModel.DataAnnotations;
public enum MyEnum
{
[Display(ResourceType = typeof(MyResources), Name = nameof(MyResources.FirstValueDescription))]
FirstValue = 0,
}