How to define an enum with string value?

后端 未结 18 1409
一生所求
一生所求 2020-12-12 14:53

I am trying to define an Enum and add valid common separators which used in CSV or similar files. Then I am going to bind it to a ComboBox as a dat

18条回答
  •  鱼传尺愫
    2020-12-12 15:05

    Building on some of the answers here I have implemented a reusable base class that mimics the behaviour of an enum but with string as the underlying type. It supports various operations including:

    1. getting a list of possible values
    2. converting to string
    3. comparison with other instances via .Equals, ==, and !=
    4. conversion to/from JSON using a JSON.NET JsonConverter

    This is the base class in it's entirety:

    public abstract class StringEnumBase : IEquatable
        where T : StringEnumBase
    {
        public string Value { get; }
    
        protected StringEnumBase(string value) => this.Value = value;
    
        public override string ToString() => this.Value;
    
        public static List AsList()
        {
            return typeof(T)
                .GetProperties(BindingFlags.Public | BindingFlags.Static)
                .Where(p => p.PropertyType == typeof(T))
                .Select(p => (T)p.GetValue(null))
                .ToList();
        }
    
        public static T Parse(string value)
        {
            List all = AsList();
    
            if (!all.Any(a => a.Value == value))
                throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");
    
            return all.Single(a => a.Value == value);
        }
    
        public bool Equals(T other)
        {
            if (other == null) return false;
            return this.Value == other?.Value;
        }
    
        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            if (obj is T other) return this.Equals(other);
            return false;
        }
    
        public override int GetHashCode() => this.Value.GetHashCode();
    
        public static bool operator ==(StringEnumBase a, StringEnumBase b) => a?.Equals(b) ?? false;
    
        public static bool operator !=(StringEnumBase a, StringEnumBase b) => !(a?.Equals(b) ?? false);
    
        public class JsonConverter : Newtonsoft.Json.JsonConverter
            where T : StringEnumBase
        {
            public override bool CanRead => true;
    
            public override bool CanWrite => true;
    
            public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));
    
            private static bool ImplementsGeneric(Type type, Type generic)
            {
                while (type != null)
                {
                    if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                        return true;
    
                    type = type.BaseType;
                }
    
                return false;
            }
    
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                JToken item = JToken.Load(reader);
                string value = item.Value();
                return StringEnumBase.Parse(value);
            }
    
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                if (value is StringEnumBase v)
                    JToken.FromObject(v.Value).WriteTo(writer);
            }
        }
    }
    

    And this is how you would implement your "string enum":

    [JsonConverter(typeof(JsonConverter))]
    public class Colour : StringEnumBase
    {
        private Colour(string value) : base(value) { }
    
        public static Colour Red => new Colour("red");
        public static Colour Green => new Colour("green");
        public static Colour Blue => new Colour("blue");
    }
    

    Which could be used like this:

    public class Foo
    {
        public Colour colour { get; }
    
        public Foo(Colour colour) => this.colour = colour;
    
        public bool Bar()
        {
            if (this.colour == Colour.Red || this.colour == Colour.Blue)
                return true;
            else
                return false;
        }
    }
    

    I hope someone finds this useful!

提交回复
热议问题