使用 EnumMemberAttribute 并进行自动字符串转换

问题描述 投票:0回答:6

我有以下代码

[DataContract]
public enum StatusType
{
    [EnumMember(Value = "A")]
    All,
    [EnumMember(Value = "I")]
    InProcess,
    [EnumMember(Value = "C")]
    Complete,
}

我想做以下事情:

 var s = "C";
 StatusType status = SerializerHelper.ToEnum<StatusType>(s);   //status is now StatusType.Complete
 string newString = SerializerHelper.ToEnumString<StatusType>(status);   //newString is now "C"

我已经使用 DataContractSerializer 完成了第二部分(请参阅下面的代码),但看起来工作量很大。

我是否遗漏了一些明显的东西?有想法吗?谢谢。

    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;
        }
    }
c# enums
6个回答
49
投票

这是我的建议 - 它应该让您了解如何执行此操作(另请检查获取枚举值的属性):

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

17
投票

如果您的项目引用了 Newtonsoft.Json(现在没有引用什么?!),那么有一个简单的一行解决方案,不需要反射:

public static string ToEnumString<T>(T value)
{
   return JsonConvert.SerializeObject(value).Replace("\"", "");
}

public static T ToEnum<T>(string value)
{
   return JsonConvert.DeserializeObject<T>($"\"{value}\"");
}

仅当您在

ToEnumString
中注册了
StringEnumConverter
时,
JsonSerializerSettings
方法才有效(请参阅JavaScriptSerializer - 枚举作为字符串的 JSON 序列化),例如

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    Converters = { new StringEnumConverter() }
};

此方法的另一个优点是,如果只有某些枚举元素具有 member 属性,事情仍然按预期工作,例如

public enum CarEnum
{
    Ford,
    Volkswagen,
    [EnumMember(Value = "Aston Martin")]
    AstonMartin
}

9
投票

使用扩展和 C# 7.3 约束

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

3
投票

您可以使用反射来获取

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

根据您的

ToEnum
的工作方式,您可能也想使用这种方法。此外,在调用
ToEnumString
时可以推断类型,例如
SerializerHelper.ToEnumString(status);


1
投票

此示例展示了如何使用

DescriptionAttribute
EnumMemberAttribute
和属性名称转换枚举:

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

N 单元测试:

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

0
投票

就个人而言,我使用 Reefact.JsonEnumValueBinding 库,因为我现在主要使用 Json 而不是 Xml。这样合适吗? ?

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

因此您可以执行以下操作:

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"

不幸的是,Json 序列化引入了引号,但这也许不会在您的实际用例中给您带来任何真正的问题?

© www.soinside.com 2019 - 2024. All rights reserved.