This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding an enum value by its Description Attribute
I have a generic extension method which gets the Description attribute from an Enum:
enum Animal
{
[Description("")]
NotSet = 0,
[Description("Giant Panda")]
GiantPanda = 1,
[Description("Lesser Spotted Anteater")]
LesserSpottedAnteater = 2
}
public static string GetDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute
= Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
so I can do...
string myAnimal = Animal.GiantPanda.GetDescription(); // = "Giant Panda"
now, I'm trying to work out the equivalent function in the other direction, something like...
Animal a = (Animal)Enum.GetValueFromDescription("Giant Panda", typeof(Animal));
public static class EnumEx
{
public static T GetValueFromDescription<T>(string description) where T : Enum
{
foreach(var field in typeof(T).GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
{
if (attribute.Description == description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException("Not found.", nameof(description));
// Or return default(T);
}
}
Usage:
var panda = EnumEx.GetValueFromDescription<Animal>("Giant Panda");
rather than extension methods, just try a couple of static methods
public static class Utility
{
public static string GetDescriptionFromEnumValue(Enum value)
{
DescriptionAttribute attribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false)
.SingleOrDefault() as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
public static T GetEnumValueFromDescription<T>(string description)
{
var type = typeof(T);
if (!type.IsEnum)
throw new ArgumentException();
FieldInfo[] fields = type.GetFields();
var field = fields
.SelectMany(f => f.GetCustomAttributes(
typeof(DescriptionAttribute), false), (
f, a) => new { Field = f, Att = a })
.Where(a => ((DescriptionAttribute)a.Att)
.Description == description).SingleOrDefault();
return field == null ? default(T) : (T)field.Field.GetRawConstantValue();
}
}
and use here
var result1 = Utility.GetDescriptionFromEnumValue(
Animal.GiantPanda);
var result2 = Utility.GetEnumValueFromDescription<Animal>(
"Lesser Spotted Anteater");
The solution works good except if you have a Web Service.
You would need to do the Following as the Description Attribute is not serializable.
[DataContract]
public enum ControlSelectionType
{
[EnumMember(Value = "Not Applicable")]
NotApplicable = 1,
[EnumMember(Value = "Single Select Radio Buttons")]
SingleSelectRadioButtons = 2,
[EnumMember(Value = "Completely Different Display Text")]
SingleSelectDropDownList = 3,
}
public static string GetDescriptionFromEnumValue(Enum value)
{
EnumMemberAttribute attribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(EnumMemberAttribute), false)
.SingleOrDefault() as EnumMemberAttribute;
return attribute == null ? value.ToString() : attribute.Value;
}
Should be pretty straightforward, its just the reverse of your previous method;
public static int GetEnumFromDescription(string description, Type enumType)
{
foreach (var field in enumType.GetFields())
{
DescriptionAttribute attribute
= Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))as DescriptionAttribute;
if(attribute == null)
continue;
if(attribute.Description == description)
{
return (int) field.GetValue(null);
}
}
return 0;
}
Usage:
Console.WriteLine((Animal)GetEnumFromDescription("Giant Panda",typeof(Animal)));
You can't extend Enum as it's a static class. You can only extend instances of a type. With this in mind, you're going to have to create a static method yourself to do this; the following should work when combined with your existing method GetDescription:
public static class EnumHelper
{
public static T GetEnumFromString<T>(string value)
{
if (Enum.IsDefined(typeof(T), value))
{
return (T)Enum.Parse(typeof(T), value, true);
}
else
{
string[] enumNames = Enum.GetNames(typeof(T));
foreach (string enumName in enumNames)
{
object e = Enum.Parse(typeof(T), enumName);
if (value == GetDescription((Enum)e))
{
return (T)e;
}
}
}
throw new ArgumentException("The value '" + value
+ "' does not match a valid enum name or description.");
}
}
And the usage of it would be something like this:
Animal giantPanda = EnumHelper.GetEnumFromString<Animal>("Giant Panda");
You need to iterate through all the enum values in Animal and return the value that matches the description you need.
Related
I have a StringValue attribute for enums values, so I could attach a description to each value:
public class StringValueAttribute : Attribute
{
public string Value { get; private set; }
public StringValueAttribute(string value)
{
Value = value;
}
}
And that is how I use it:
enum Group
{
[StringValue("Computer Science")]
ComputerScience,
[StringValue("Software Engineering")]
SoftwareEngineering,
// ... additional values follow.
}
I have a method that retrieves the StringValue given the enum value:
public static string GetStringValue(Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(type.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
I want to have another method that gets an enum (the enum itself, not a value) and retrieves an IEnumerable using the GetStringValue method. I am not sure how to accomplish that. How could a method like this look like?
Edit: this question is not a duplicate of How to get C# Enum description from value?. I know how to get an enum attribute value, and I actually have a method in the question that does exactly that. My question is how to enumerate all the attributes in an enum.
The most straightforward way to do this is with a generic, though you could always pass in an instance of your specific Enum, get its type, then return the StringValue values for all its values:
public static class EnumExtensions
{
public static IEnumerable<string> GetStringValues<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
{
return Enum.GetValues(typeof(TEnum))
.Cast<Enum>()
.Select(e => e.GetStringValue())
.ToList();
}
public static IEnumerable<string> GetStringValuesOfType(Enum value)
{
return Enum.GetValues(value.GetType())
.Cast<Enum>()
.Select(e => e.GetStringValue())
.ToList();
}
public static string GetStringValue(this Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
}
Notes:
There is no where TEnum : Enum constraint in c#. Restricting TEnum to be struct, IConvertible, IComparable, IFormattable is mostly sufficient.
That being said, there is a cunning trick to apply an enum constraint which is shown in this answer to Enum type constraints in C# by SLaks. (I didn't use it in this answer though since it's really very cunning.)
As noted in #EdPlunkett's comment you need to pass value.ToString() to type.GetField() since you're getting the field corresponding to that specific incoming enum value.
Sample fiddle
This should work:
static void Main(string[] args)
{
foreach (var item in GetStringNames<Group>())
{
Console.WriteLine(item);
}
}
public static string GetStringValue(Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
public static IEnumerable<string> GetStringNames<T>()
{
var type = typeof(T);
if (type.IsEnum == false)
{
throw new ArgumentException("T must be an Enum type");
}
var values = type.GetEnumValues();
foreach (var item in values)
{
yield return GetStringValue((Enum)item);
}
}
public enum Status
{
Pending,
[EnumMember(Value = "In Progress")]
InProgress,
Failed,
Success
}
string dbValue = "In Progress";
if (dbValue == ValueOf(Status.InProgress)){
//do some thing
}
How do I read the Value of Status.InProgress so I get back "in Progress"?
This is an extension method that works with C# 8 and nullable reference types:
public static string? GetEnumMemberValue<T>(this T value)
where T : Enum
{
return typeof(T)
.GetTypeInfo()
.DeclaredMembers
.SingleOrDefault(x => x.Name == value.ToString())
?.GetCustomAttribute<EnumMemberAttribute>(false)
?.Value;
}
Original Answer:
I've adapted this for .NET Core. Here it is:
public static String GetEnumMemberValue<T>(T value)
where T : struct, IConvertible
{
return typeof(T)
.GetTypeInfo()
.DeclaredMembers
.SingleOrDefault(x => x.Name == value.ToString())
?.GetCustomAttribute<EnumMemberAttribute>(false)
?.Value;
}
Something like this:
public string GetEnumMemberAttrValue(Type enumType, object enumVal)
{
var memInfo = enumType.GetMember(enumVal.ToString());
var attr = memInfo[0].GetCustomAttributes(false).OfType<EnumMemberAttribute>().FirstOrDefault();
if(attr != null)
{
return attr.Value;
}
return null;
}
Usage:
var enumType = typeof(Status);
var enumVal = Status.InProgress;
var str = GetEnumMemberAttrValue(enumType,enumVal);
Borrowing from Amir's answer, a slightly nicer version is possible using generics as follows:
public string GetEnumMemberAttrValue<T>(T enumVal)
{
var enumType = typeof(T);
var memInfo = enumType.GetMember(enumVal.ToString());
var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType<EnumMemberAttribute>().FirstOrDefault();
if (attr != null)
{
return attr.Value;
}
return null;
}
Usage as follows:
var enumVal = Status.InProgress;
var str = GetEnumMemberAttrValue(enumVal);
As far as I'm aware T can't be constrained to enum's using a where clause. I would be happy to be corrected though.
Wrapped it in an extension to make it feel more natural:
public static class Extension
{
public static string ToEnumMemberAttrValue(this Enum #enum)
{
var attr =
#enum.GetType().GetMember(#enum.ToString()).FirstOrDefault()?.
GetCustomAttributes(false).OfType<EnumMemberAttribute>().
FirstOrDefault();
if (attr == null)
return #enum.ToString();
return attr.Value;
}
}
Usage:
string dbValue = "In Progress";
if (dbValue == Status.ToEnumMemberAttrValue())){
//do some thing
}
If you have Newtonsoft in your project, that is what you should do:
to the Enum, you should add the attribute [JsonConverter(typeof(StringEnumConverter))]
and now you can call the JsonConvertor to serialize your value as the member string value.
in your example, it should be like that
[JsonConverter(typeof(StringEnumConverter))]
public enum Status
{
Pending,
[EnumMember(Value = "In Progress")]
InProgress,
Failed,
Success
}
string dbValue = "In Progress";
if (dbValue == JsonConvert.SerializeObject(Status.InProgress)){
//do some thing
}
Note as mentioned by #Dinesh in the comment that the string is JSON therefor return with quatos so you can workaround that to get a clean string with strin.Replace method as:
dbValue == JsonConvert.SerializeObject(Status.InProgress).Replace("\"","");
public static object GetMemberAttr(this Enum enumItem)
{
var memInfo = enumItem.GetType().GetMember(enumItem.ToString());
var attr = memInfo[0].GetCustomAttributes(false);
return attr == null || attr.Length == 0 ? null :((System.Runtime.Serialization.EnumMemberAttribute) attr[0]).Value;
}
Usage: {YouEnum}.{EnumItem}.GetMemberAttr()
public enum TEST_ENUM
{
[EnumMember(Value = "1min")]
Minute,
[EnumMember(Value = "2min")]
TwoMinutes,
[EnumMember(Value = "3min")]
ThreeMinutes,
}
public TEST_ENUM t;
? t.TwoMinutes.GetMemberAttr()
2min
Try This ,
var type = typeof(YourEnum);
var Info = type.GetMember(YourEnum.attribute); // pass enum item here
string enumdescription = Info[0].CustomAttributes.SingleOrDefault().NamedArguments[0].TypedValue.ToString();
Status.InProgress.GetStringValue()
If you have the Elasticsearch.Net library you can use this, but the other solutions here are probably better if you dont happen to already be using ES.
[EnumMember] attribute is used by a serializer such as Newtonsoft.
System.Runtime.Serialization (namespace)
EnumMemberAttribute (class)
Enum:
public enum Status
{
Pending,
[EnumMember(Value = "In Progress")]
InProgress,
Failed,
Success
}
Example code:
string databaseValue = "In Progress";
// Serialize the enum value
string statusValue = Newtonsoft.Json.JsonConvert.SerializeObject(Status.InProgress);
if (statusValue.Contains(databaseValue))
{
// Do something
}
A problem with all of the approaches post so far is that they use GetCustomAttribute<EnumMemberAttribute> for every lookup, which is somewhat expensive.
Given that attributes loaded from reflection are immutable it makes sense to cache the GetCustomAttribute<> result in-memory for each enum type (TEnum) being looked-up.
A static class<T> that's generic over T with a static initializer effectively acts as a singleton for every T, which can be used to own an ImmutableDictionary to efficiently and lazily cache the enum member attribute data:
Because the code below is generic over TEnum : struct, Enum it means there is also no boxing of enum values and it supports enums of varying underlying-type (e.g. enum Foo : int, enum Bar : long, etc):
public static class EnumMemberNames
{
public static String? GetNameOrNull<TEnum>( TEnum value ) => EnumAttribCache<TEnum>.cachedNames.TryGetValue( value, out String? text ) ? text : null;
public static String GetName<TEnum>( TEnum value ) => GetNameOrNull( value ) ?? value.ToString();
private static class EnumAttribCache<TEnum>
where TEnum : struct, Enum
{
public static readonly ImmutableDictionary<TEnum,String> cachedNames = LoadNames();
private static ImmutableDictionary<TEnum,String> LoadNames()
{
return typeof(TEnum)
.GetTypeInfo()
.DeclaredFields
.Where( f => f.IsStatic && f.IsPublic && f.FieldType == typeof(TEnum) )
.Select( f => ( field: f, attrib: f.GetCustomAttribute<EnumMemberAttribute>() ) )
.Where( t => ( t.attrib?.IsValueSetExplicitly ?? false ) && !String.IsNullOrEmpty( t.attrib.Value ) )
.ToDictionary(
keySelector : t => (TEnum)t.field.GetValue( obj: null )!,
elementSelector: t => t.attrib!.Value!
)
.ToImmutableDictionary();
}
}
}
Used like so:
enum Foo
{
[EnumMemberAttribute( Value = "first" )]
A = 1,
[EnumMemberAttribute( Value = "second" )]
B = 2,
Unnamed = 4
}
public static void Main()
{
Console.WriteLine( EnumMemberNames.GetNameOrNull( Foo.A ) ); // "first"
Console.WriteLine( EnumMemberNames.GetNameOrNull( Foo.B ) ); // "second"
Console.WriteLine( EnumMemberNames.GetName( Foo.Unnamed ) ); // "Unnamed"
}
I have the following ENUM:
[Flags]
public enum DataFiat {
[Description("Público")]
Public = 1,
[Description("Filiado")]
Listed = 2,
[Description("Cliente")]
Client = 4
} // DataFiat
And I created an extension to get an Enum attribute:
public static T GetAttribute<T>(this Enum value) where T : Attribute {
T attribute;
MemberInfo info = value.GetType().GetMember(value.ToString()).FirstOrDefault();
if (info != null) {
attribute = (T)info.GetCustomAttributes(typeof(T), false).FirstOrDefault();
return attribute;
}
return null;
}
This works for non Flags Enums ... But when I have:
var x = DataFiat.Public | DataFiat.Listed;
var y = x.GetAttribute<Description>();
The value of y is null ...
I would like to get "Público, Filiado, Cliente" ... Just as ToString() works.
How can I change my extension to make this work?
Thank You
You can use this:
var values = x.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (DataFiat)Enum.Parse(typeof(DataFiat), v));
To get the individual values. Then get the attribute values of them.
Something like this:
var y2 = values.GetAttributes<DescriptionAttribute, DataFiat>();
public static T[] GetAttributes<T, T2>(this IEnumerable<T2> values) where T : Attribute
{
List<T> ts =new List<T>();
foreach (T2 value in values)
{
T attribute;
MemberInfo info = value.GetType().GetMember(value.ToString()).FirstOrDefault();
if (info != null)
{
attribute = (T)info.GetCustomAttributes(typeof(T), false).FirstOrDefault();
ts.Add(attribute);
}
}
return ts.ToArray();
}
in .NET CORE without any additional libraries you can do:
public enum Divisions
{
[Display(Name = "My Title 1")]
None,
[Display(Name = "My Title 2")]
First,
}
and to get the title:
using System.ComponentModel.DataAnnotations
using System.Reflection
string title = enumValue.GetType()?.GetMember(enumValue.ToString())?[0]?.GetCustomAttribute<DisplayAttribute>()?.Name;
I think you want to make something like that
using System;
public enum ArrivalStatus { Unknown=-3, Late=-1, OnTime=0, Early=1 };
public class Example
{
public static void Main()
{
int[] values = { -3, -1, 0, 1, 5, Int32.MaxValue };
foreach (var value in values)
{
ArrivalStatus status;
if (Enum.IsDefined(typeof(ArrivalStatus), value))
status = (ArrivalStatus) value;
else
status = ArrivalStatus.Unknown;
Console.WriteLine("Converted {0:N0} to {1}", value, status);
}
}
}
// The example displays the following output:
// Converted -3 to Unknown
// Converted -1 to Late
// Converted 0 to OnTime
// Converted 1 to Early
// Converted 5 to Unknown
// Converted 2,147,483,647 to Unknown
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
public static class Program
{
[Flags]
public enum DataFiat
{
[Description("Público")]
Public = 1,
[Description("Filiado")]
Listed = 2,
[Description("Cliente")]
Client = 4
}
public static ICollection<string> GetAttribute<T>(this Enum value)
{
var result = new Collection<string>();
var type = typeof(DataFiat);
foreach (var value1 in Enum.GetValues(type))
{
var memInfo = type.GetMember(value1.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
var description = ((DescriptionAttribute)attributes[0]).Description;
result.Add(description);
}
return result;
}
static void Main(string[] args)
{
var x = DataFiat.Public | DataFiat.Listed;
var y = x.GetAttribute<DataFiat>();
var output = string.Join(" ", y.ToArray());
Console.WriteLine(output);
}
}
I have changed the T to ICollection but you can change it as you wish or you can merege the data within the method and return the string back.
I came up with a different solution based on my previous code. It can be used as follows:
DataFiat fiat = DataFiat.Public | DataFiat.Listed;
var b = fiat.ToString();
var c = fiat.GetAttributes<TextAttribute>();
var d = fiat.GetAttributes<TextAttribute, String>(x => String.Join(",", x.Select(y => y.Value)));
I think it becomes easy to use either to get the attributes or doing something with them.
What do you think?
Let me know if the code can be somehow improved. Here is the code:
public static List<T> GetAttributes<T>(this Enum value) where T : Attribute {
List<T> attributes = new List<T>();
IEnumerable<Enum> flags = Enum.GetValues(value.GetType()).Cast<Enum>().Where(value.HasFlag);
if (flags != null) {
foreach (Enum flag in flags) {
MemberInfo info = flag.GetType().GetMember(flag.ToString()).FirstOrDefault();
if (info != null)
attributes.Add((T)info.GetCustomAttributes(typeof(T), false).FirstOrDefault());
}
return attributes;
}
return null;
} // GetAttributes
public static Expected GetAttributes<T, Expected>(this Enum value, Func<List<T>, Expected> expression) where T : Attribute {
List<T> attributes = value.GetAttributes<T>();
if (attributes == null)
return default(Expected);
return expression(attributes);
} // GetAttributes
I have the following code
[DataContract]
public enum StatusType
{
[EnumMember(Value = "A")]
All,
[EnumMember(Value = "I")]
InProcess,
[EnumMember(Value = "C")]
Complete,
}
I'd like to do the following:
var s = "C";
StatusType status = SerializerHelper.ToEnum<StatusType>(s); //status is now StatusType.Complete
string newString = SerializerHelper.ToEnumString<StatusType>(status); //newString is now "C"
I've done the second part using DataContractSerializer (see code below), but it seems like a lot of work.
Am I missing something obvious? Ideas? Thanks.
public static string ToEnumString<T>(T type)
{
string s;
using (var ms = new MemoryStream())
{
var ser = new DataContractSerializer(typeof(T));
ser.WriteObject(ms, type);
ms.Position = 0;
var sr = new StreamReader(ms);
s = sr.ReadToEnd();
}
using (var xml = new XmlTextReader(s, XmlNodeType.Element, null))
{
xml.MoveToContent();
xml.Read();
return xml.Value;
}
}
Here is my proposition - it should give you the idea on how to do this (check also Getting attributes of Enum's value):
public static string ToEnumString<T>(T type)
{
var enumType = typeof (T);
var name = Enum.GetName(enumType, type);
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
return enumMemberAttribute.Value;
}
public static T ToEnum<T>(string str)
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name);
}
//throw exception or whatever handling you want or
return default(T);
}
If your project references Newtonsoft.Json (what doesn't these days?!), then there is a simple one line solution that doesn't need reflection:
public static string ToEnumString<T>(T value)
{
return JsonConvert.SerializeObject(value).Replace("\"", "");
}
public static T ToEnum<T>(string value)
{
return JsonConvert.DeserializeObject<T>($"\"{value}\"");
}
The ToEnumString method will only work if you have the StringEnumConverter registered in your JsonSerializerSettings (see JavaScriptSerializer - JSON serialization of enum as string), e.g.
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Converters = { new StringEnumConverter() }
};
Another advantage of this method is that if only some of your enum elements have the member attribute, things still work as expected, e.g.
public enum CarEnum
{
Ford,
Volkswagen,
[EnumMember(Value = "Aston Martin")]
AstonMartin
}
Using extensions and C# 7.3 constraints
public static class EnumMemberExtensions
{
public static string ToEnumString<T>(this T type)
where T : Enum
{
var enumType = typeof(T);
var name = Enum.GetName(enumType, type);
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
return enumMemberAttribute.Value;
}
public static T ToEnum<T>(this string str)
where T : Enum
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name);
}
//throw exception or whatever handling you want or
return default;
}
}
You can use reflection to get the value of the EnumMemberAttribute.
public static string ToEnumString<T>(T instance)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("instance", "Must be enum type");
string enumString = instance.ToString();
var field = typeof(T).GetField(enumString);
if (field != null) // instance can be a number that was cast to T, instead of a named value, or could be a combination of flags instead of a single value
{
var attr = (EnumMemberAttribute)field.GetCustomAttributes(typeof(EnumMemberAttribute), false).SingleOrDefault();
if (attr != null) // if there's no EnumMember attr, use the default value
enumString = attr.Value;
}
return enumString;
}
Depending on how your ToEnum works, you might want to use this sort of approach there as well. Also, the type can be inferred when calling ToEnumString, e.g. SerializerHelper.ToEnumString(status);
This example shows how to convert enums using the DescriptionAttribute, the EnumMemberAttribute and the property name:
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public static class EnumExtensions
{
public static T ToEnumByAttributes<T>(this string value)
where T:Enum
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var field = enumType.GetField(name);
if(field == null) continue;
var enumMemberAttribute = GetEnumMemberAttribute(field);
if (enumMemberAttribute != null && enumMemberAttribute.Value == value)
{
return (T)Enum.Parse(enumType, name);
}
var descriptionAttribute = GetDescriptionAttribute(field);
if (descriptionAttribute != null && descriptionAttribute.Description == value)
{
return (T)Enum.Parse(enumType, name);
}
if (name == value)
{
return (T)Enum.Parse(enumType, name);
}
}
throw new ArgumentOutOfRangeException(nameof(value), value, $"The value could not be mapped to type {enumType.FullName}");
}
public static string ToStringByAttributes(this Enum value)
{
var field = value
.GetType()
.GetField(value.ToString());
if (field == null) return string.Empty;
var enumMemberAttribute = GetEnumMemberAttribute(field);
if (enumMemberAttribute != null)
{
return enumMemberAttribute.Value ?? string.Empty;
}
var descriptionAttribute = GetDescriptionAttribute(field);
if (descriptionAttribute != null)
{
return descriptionAttribute.Description;
}
return value.ToString();
}
private static DescriptionAttribute? GetDescriptionAttribute(FieldInfo field)
{
return field
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.OfType<DescriptionAttribute>()
.SingleOrDefault();
}
private static EnumMemberAttribute? GetEnumMemberAttribute(FieldInfo field)
{
return field
.GetCustomAttributes(typeof(EnumMemberAttribute), false)
.OfType<EnumMemberAttribute>()
.SingleOrDefault();
}
}
NUnit Tests:
[TestFixture]
public sealed class EnumExtensionsTests
{
public enum TestEnum
{
[EnumMember(Value = "A")]
Alpha,
[Description("O")]
Omega
}
[Test]
public void ShouldSerialize_FromEnumAttribute()
{
var result = TestEnum.Alpha.ToStringByAttributes();
Assert.That(result, Is.EqualTo("A"));
}
[Test]
public void ShouldSerialize_FromDescriptionAttribute()
{
var result = TestEnum.Omega.ToStringByAttributes();
Assert.That(result, Is.EqualTo("O"));
}
[Test]
public void ShouldDeserialize_FromEnumAttribute()
{
var result = "A".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Alpha));
}
[Test]
public void ShouldDeserialize_FromDescriptionAttribute()
{
var result = "O".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Omega));
}
[Test]
public void ShouldDeserialize_FromPropertyName()
{
var result = "Alpha".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Alpha));
}
}
So we have our enums setup like this:
[CorrelatedNumeric(0)]
[Description("USD")]
[SequenceNumber(10)]
USD = 247
Basically, another function can provide the string "USD" to me, but not the exact enum because the source of it is Excel and we can't make our users remember the enum values ;) nor would that make much sense.
Is there a way in c# to get from "USD" to 247 from having our enums setup as they are above?
Would Enum.TryParse() or Enum.Parse() do what you need?
Currency cValue = (Currency) Enum.Parse(typeof(Currency), currencyString);
Absolutely - build a Dictionary<string, YourEnumType> by reflection. Just iterate over all the fields in the enum and find the attribute values, and build up the dictionary that way.
You can see how I've done something similar in Unconstrained Melody for the description attribute, in EnumInternals:
// In the static initializer...
ValueToDescriptionMap = new Dictionary<T, string>();
DescriptionToValueMap = new Dictionary<string, T>();
foreach (T value in Values)
{
string description = GetDescription(value);
ValueToDescriptionMap[value] = description;
if (description != null && !DescriptionToValueMap.ContainsKey(description))
{
DescriptionToValueMap[description] = value;
}
}
private static string GetDescription(T value)
{
FieldInfo field = typeof(T).GetField(value.ToString());
return field.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.Select(x => x.Description)
.FirstOrDefault();
}
Just do the same thing for your own attribute type.
public static object enumValueOf(string description, Type enumType)
{
string[] names = Enum.GetNames(enumType);
foreach (string name in names)
{
if (descriptionValueOf((Enum)Enum.Parse(enumType, name)).Equals(description))
{
return Enum.Parse(enumType, name);
}
}
throw new ArgumentException("The string is not a description of the specified enum.");
}
public static string descriptionValueOf(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[]) fi.GetCustomAttributes( typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return value.ToString();
}
}