我有一个枚举如下:
public enum MyEnum { One, Two, Three}
我想在上面的枚举中削减一些字符串,例如,下面的字符串将被解析为MyEnum.Two:
"Two", "TWO", "Second", "2"
我知道我可以维护一个映射功能来完成这项工作。但是,我只是想找到一种更好的方法,例如,重写Enum.Parse函数,或类似的东西。我曾试图使用IConvertable,但似乎不可能。有什么想法吗?
答案 0 :(得分:3)
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class NameAttribute : Attribute
{
public readonly string[] Names;
public NameAttribute(string name)
{
if (name == null)
{
throw new ArgumentNullException();
}
Names = new[] { name };
}
public NameAttribute(params string[] names)
{
if (names == null || names.Any(x => x == null))
{
throw new ArgumentNullException();
}
Names = names;
}
}
public static class ParseEnum
{
public static TEnum Parse<TEnum>(string value) where TEnum : struct
{
return ParseEnumImpl<TEnum>.Values[value];
}
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
{
return ParseEnumImpl<TEnum>.Values.TryGetValue(value, out result);
}
private static class ParseEnumImpl<TEnum> where TEnum : struct
{
public static readonly Dictionary<string, TEnum> Values = new Dictionary<string,TEnum>();
static ParseEnumImpl()
{
var nameAttributes = typeof(TEnum)
.GetFields()
.Select(x => new
{
Value = x,
Names = x.GetCustomAttributes(typeof(NameAttribute), false)
.Cast<NameAttribute>()
});
var degrouped = nameAttributes.SelectMany(
x => x.Names.SelectMany(y => y.Names),
(x, y) => new { Value = x.Value, Name = y });
Values = degrouped.ToDictionary(
x => x.Name,
x => (TEnum)x.Value.GetValue(null));
}
}
}
然后你可以(注意[Name]
的双重语法,多个[Name]
或单个[Name]
的多重名称):
public enum TestEnum
{
[Name("1")]
[Name("Foo")]
[Name("F")]
[Name("XF", "YF")]
Foo = 1,
[Name("2")]
[Name("Bar")]
[Name("B")]
[Name("XB", "YB")]
Bar = 2
}
和
TestEnum r1 = ParseEnum.Parse<TestEnum>("XF");
TestEnum r2;
bool r3 = ParseEnum.TryParse<TestEnum>("YB", out r2);
请注意使用内部类(ParseEnumImpl<TEnum>
)来缓存TEnum
“名称”。
答案 1 :(得分:2)
最好的方法是使用映射存储Dictionary
:
static Dictionary<string, string> _mappings = new Dictionary<string, string>
{
{ "Two", "Two" },
{ "Second", "Two" },
{ "2", "Two" }
};
然后你调用Enum.Parse(Type, String, Boolean)
的案例介绍版本:
String str = "2";
MyEnum number = (MyEnum)Enum.Parse(typeof(MyEnum), _mappings[str], true);
通常,我更喜欢简单的解决方案,因为它们比“覆盖Enum.Parse
函数或类似的东西更容易理解”。
但是我们可以使用Dictionary<string, MyEnum>
:
static Dictionary<string, MyEnum> _mappings = new Dictionary<string, MyEnum>
{
{ "Two", MyEnum.Two },
{ "Second", MyEnum.Two },
{ "2", MyEnum.Two }
};
现在获取你的枚举:
MyEnum myEnumb = _mappings[str];
后一种方法也提高了性能,因为我们避免了Enum.Parse
调用。
答案 2 :(得分:0)
您正在尝试解析两个不同的案例:
如果这两个是输入中的唯一情况,您只需使用Enum.TryParse Method (String, Boolean, TEnum)尝试以不区分大小写的方式解析文本:
MyEnum output;
if (Enum.TryParseEnum(input,true,out output))
{
// Process succesfull value
}
从文档示例中可以看出TryParse
可以处理文本和数字字符串输入。
至于解析Second
,除了编码器的思想之外,这个文本与枚举无关。在这种情况下,你真的必须创建一个映射并将它放在某处 - 一个字典,一个自定义属性等。
实际上,如果数据来自外部文件,这是一个ETL问题而不是解析问题。在这种情况下,典型的解决方案是创建查找表,将输入映射到已识别的输出,并在解析之前用查找值替换输入
答案 3 :(得分:0)
您可以使用nuget包Enums.NET对Description Attribute进行反向查找。
请注意,其中有一个带有ignoreCase布尔值的重载。
public enum PhoneCode
{
[DescriptionAttribute("991")]
Emergency,
[DescriptionAttribute("411")]
Info,
}
PhoneCode code;
EnumsNET.Enums.TryParse("991", out code, EnumsNET.EnumFormat.Description)
答案 4 :(得分:0)
虽然我喜欢这些属性,但我自己使用了映射,而是像这样扩展了 string
基本类型:
public enum Rating
{
High,
Medium,
Low,
Other
};
然后我有一个 static
类用于我的扩展......其中包含此代码:
public static Dictionary<string, Rating> RatingsMap = new Dictionary<string, Rating>()
{
{"Highly recommended", Rating.High},
{"Ok", Rating.Medium},
{"Liked", Rating.Medium},
{"Thumbs down", Rating.Low}
};
public static Rating ToRating(this string me)
{
Rating retval = Rating.Other;
if (EXTENSIONS.RatingsMap.ContainsKey(me))
retval = EXTENSIONS.RatingsMap[me];
return retval;
}