Reputation: 3506
I am currently implementing an associacion of strings and enums based on this suggestion. That being, I have a Description
attribute associated with every enum element. On that page there is also a function which returns the description's string based on the given enum. What I would like to implement now is the reverse function, that is, given an input string lookup the enum with the corresponding description if it exists, returning null otherwise.
I have tried (T) Enum.Parse(typeof(T), "teststring")
but it throws an exception.
Upvotes: 12
Views: 15574
Reputation: 937
You could also use Humanizer for that. To get the description you write:
EAssemblyUnit.eUCAL1.Humanize();
and to get the enum back from the description, which is what you want, you can write:
"UCAL1".DehumanizeTo<EAssemblyUnit>();
Disclaimer: I am the creator of Humanizer.
Upvotes: 3
Reputation: 11
I would have upvoted Anna's answer but I don't have the reputation to do so. With part of this based on her answer here's a 2-way solution that I came up with. Supplying a defaultValue to ParseEnum method covers cases where the same Enum may have a different default based on it's usage.
public static string GetDescription<T>(this object enumerationValue) where T : struct
{
// throw an exception if enumerationValue is not an Enum
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
}
//Tries to find a DescriptionAttribute for a potential friendly name for the enum
MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
//Pull out the description value
return attributes[0].Description;
}
}
//In case we have no description attribute, we'll just return the ToString of the enum
return enumerationValue.ToString();
}
public static T ParseEnum<T>(this string stringValue, T defaultValue)
{
// throw an exception if T is not an Enum
Type type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("T must be of Enum type", "T");
}
//Tries to find a DescriptionAttribute for a potential friendly name for the enum
MemberInfo[] fields = type.GetFields();
foreach (var field in fields)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0 && attributes[0].Description == stringValue)
{
return (T)Enum.Parse(typeof(T), field.Name);
}
}
//In case we couldn't find a matching description attribute, we'll just return the defaultValue that we provided
return defaultValue;
}
Upvotes: 1
Reputation: 38778
You have to write your own reverse method. The stock Parse() method obviously doesn't know about your description attributes.
Something like this should work:
public static T GetEnumValueFromDescription<T>(string description)
{
MemberInfo[] fis = typeof(T).GetFields();
foreach (var fi in fis)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0 && attributes[0].Description == description)
return (T)Enum.Parse(typeof(T), fi.Name);
}
throw new Exception("Not found");
}
You'll want to find a better thing to do than throw an exception if the enum value wasn't found, though. :)
Upvotes: 19
Reputation: 241789
static string GetEnumDescription<T>(T value) {
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false
);
if (attributes != null &&
attributes.Length > 0) {
return attributes[0].Description;
}
else {
return value.ToString();
}
}
static T ParseDescriptionToEnum<T>(string description) {
Array array = Enum.GetValues(typeof(T));
var list = new List<T>(array.Length);
for(int i = 0; i < array.Length; i++) {
list.Add((T)array.GetValue(i));
}
var dict = list.Select(v => new {
Value = v,
Description = GetEnumDescription(v) }
)
.ToDictionary(x => x.Description, x => x.Value);
return dict[description];
}
I have made no attempt at error checking. Note that the dictionary doesn't need to be created on every call to the method, but I'm too lazy to fix that.
Usage:
enum SomeEnum {
[Description("First Value")]
FirstValue,
SecondValue
}
SomeEnum value = ParseDescriptionToEnum<SomeEnum>("First Value");
A test that passes:
[Fact]
public void Can_parse_a_value_with_a_description_to_an_enum() {
string description = "First Value";
SomeEnum value = ParseDescriptionToEnum<SomeEnum>(description);
Assert.Equal(SomeEnum.FirstValue, value);
}
[Fact]
public void Can_parse_a_value_without_a_description_to_an_enum() {
string description = "SecondValue";
SomeEnum value = ParseDescriptionToEnum<SomeEnum>(description);
Assert.Equal(SomeEnum.SecondValue, value);
}
Upvotes: 2
Reputation: 16728
This answer to a related question shows how to retrieve the attributes for a given type. You might use a similar approach to compare a given string to an Enum
's description attributes.
Upvotes: 0