36

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;
        }
    }
1

6 Answers 6

54

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);
}
Sign up to request clarification or add additional context in comments.

Comments

20

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
}

2 Comments

There is an overload for string JsonConvert.SerializeObject(object? value, params JsonConvert[] converters). So ToEnumString can be changed to: return JsonConvert.SerializeObject(value, new StringEnumConverter()).Replace("\"", ""); This negates the need to register the converter separately.
A great addition to the proposed solution and avoiding the need to register another convertor - works a treat!
10

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;
        }
    }

1 Comment

Is there a .net core solution equivalent to this?
4

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);

Comments

2

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));
        }
    }

Comments

0

Personally, I use the Reefact.JsonEnumValueBinding library because I mostly use Json instead of Xml nowdays. Would this be suitable? ?

public enum StatusType {
    [JsonEnumValue("A")] All,
    [JsonEnumValue("I")] InProcess,
    [JsonEnumValue("C")] Complete,
}

So you can do the following:

JsonSerializerOptions options = new() { Converters = { new JsonEnumValueConverterFactory() } };

string     s                  = "C";                                                         // s is "C"
StatusType deserializedStatus = JsonSerializer.Deserialize<StatusType>($"\"{s}\"", options); // status is now StatusType.Complete
string     serializedStatus   = JsonSerializer.Serialize(deserializedStatus, options);       // serializedStatus is now "\"C\"" 
string     newString          = serializedStatus[1..^1];                                     // newString is now "C"

Unfortunately, Json serialization introduces quotation marks, but perhaps this doesn't pose any real problems for you in your real-life use case?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.