how can i use switch statement on type-safe enum pattern

后端 未结 3 1175
被撕碎了的回忆
被撕碎了的回忆 2021-02-05 09:51

I found a goodlooking example about implementation enums in a different way. That is called type-safe enum pattern i think. I started using it but i realized th

相关标签:
3条回答
  • 2021-02-05 10:02

    The type-safe enum pattern is interesting because you can add behavior to individual enum members (which are instances). So, if the behavior you want to switch-on could be part of the class, just use polymorphism. Note that you might need to create subclasses for each member that overrides the behavior:

    public class MyState {
    
      public static readonly MyState Passed = new MyStatePassed();
      public static readonly MyState Failed = new MyStateFailed();
    
      public virtual void SomeLogic() {
        // default logic, or make it abstract
      }
    
      class MyStatePassed : MyState {
        public MyStatePassed() : base(1, "OK") { }
      }
      class MyStateFailed : MyState {
        public MyStateFailed() : base(2, "Error") { }
        public override void SomeLogic() { 
          // Error specific logic!
        }
      }
    
      ...
    }
    

    Usage:

    MyState state = ...
    state.someLogic();
    

    Now, if the logic clearly doesn't belong and you really want to switch, my advice is to create a sibling enum:

    public enum MyStateValue { 
      Passed = 1, Failed = 2
    }
    public sealed class MyState {
      public static readonly MyState Passed = new MyState(MyStateValue.Passed, "OK");
      public static readonly MyState Failed = new MyState(MyStateValue.Failed, "Error");
    
      public MyStateValue Value { get; private set; }
    
      private MyState(MyStateValue value, string name) {
        ...
      }
    }
    

    And switch on that:

    switch (state.Value) {
      case MyStateValue.Passed: ...
      case MyStateValue.Failed: ...
    }
    

    In this case, if the type-safe enum class doesn't have any behavior, there's not much reason for it to exist in place of the enum itself. But of course, you can have logic and a sibling enum at the same time.

    0 讨论(0)
  • 2021-02-05 10:14

    You can try something like this:

    class Program
    {
        static void Main(string[] args)
        {
            Gender gender = Gender.Unknown;
    
            switch (gender)
            {
                case Gender.Enum.Male:
                    break;
                case Gender.Enum.Female:
                    break;
                case Gender.Enum.Unknown:
                    break;
            }
        }
    }
    
    public class Gender : NameValue
    {
        private Gender(int value, string name)
            : base(value, name)
        {
        }
    
        public static readonly Gender Unknown = new Gender(Enum.Unknown, "Unknown");
        public static readonly Gender Male = new Gender(Enum.Male, "Male");
        public static readonly Gender Female = new Gender(Enum.Female, "Female");
        public class Enum
        {
            public const int Unknown = -1;
            public const int Male = 1;
            public const int Female = 2;
        }
    
    }
    
    public abstract class NameValue
    {
        private readonly int _value;
        private readonly string _name;
    
        protected NameValue(int value, string name)
        {
            _value = value;
            _name = name;
        }
    
        public int Value
        {
            get { return _value; }
        }
    
        public string Name
        {
            get { return _name; }
        }
    
        public override string ToString()
        {
            return Name;
        }
        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
    
        public override bool Equals(object obj)
        {
            NameValue other = obj as NameValue;
            if (ReferenceEquals(other, null)) return false;
            return this.Value == other.Value;
        }
    
        public static implicit operator int(NameValue nameValue)
        {
            return nameValue.Value;
        }
    }
    
    0 讨论(0)
  • 2021-02-05 10:24

    Jordão has the right idea, but there is a better way to implement the polymorphism, use delegate.

    The use of delegates is faster than a switch statement. (In fact, I am a strong believer that the only place for switch statements in object-oriented development is in a factory method. I always look for some sort of polymorphism to replace any switch statements in any code i deal with.)

    For example, if you want a specific behavior based on a type-safe-enum, the following pattern is what I use:

    public sealed class EnumExample
    {
        #region Delegate definitions
        /// <summary>
        /// This is an example of adding a method to the enum. 
        /// This delegate provides the signature of the method.
        /// </summary>
        /// <param name="input">A parameter for the delegate</param>
        /// <returns>Specifies the return value, in this case a (possibly 
        /// different) EnumExample</returns>
        private delegate EnumExample DoAction(string input);
        #endregion
    
        #region Enum instances
        /// <summary>
        /// Description of the element
        /// The static readonly makes sure that there is only one immutable 
        /// instance of each.
        /// </summary>
        public static readonly EnumExample FIRST = new EnumExample(1,
            "Name of first value",    
            delegate(string input)
               {
                   // do something with input to figure out what state comes next
                   return result;
               }
        );
        ...
        #endregion
    
        #region Private members
        /// <summary>
        /// The string name of the enum
        /// </summary>
        private readonly string name;
        /// <summary>
        /// The integer ID of the enum
        /// </summary>
        private readonly int value;
        /// <summary>
        /// The method that is used to execute Act for this instance
        /// </summary>
        private readonly DoAction action;
        #endregion
    
        #region Constructors
        /// <summary>
        /// This constructor uses the default value for the action method
        /// 
        /// Note all constructors are private to prevent creation of instances 
        /// by any other code
        /// </summary>
        /// <param name="value">integer id for the enum</param>
        /// <param name="name">string value for the enum</param>
        private EnumExample(int value, string name) 
                : this (value, name, defaultAction)
        {
        }
    
        /// <summary>
        /// This constructor sets all the values for a single instance.
        /// All constructors should end up calling this one.
        /// </summary>
        /// <param name="value">the integer ID for the enum</param>
        /// <param name="name">the string value of the enum</param>
        /// <param name="action">the method used to Act</param>
        private EnumExample(int value, string name, DoAction action)
        {
            this.name = name;
            this.value = value;
            this.action = action;
        }
        #endregion
    
        #region Default actions
        /// <summary>
        /// This is the default action for the DoAction delegate
        /// </summary>
        /// <param name="input">The inpute for the action</param>
        /// <returns>The next Enum after the action</returns>
        static private EnumExample defaultAction(string input)
        {
            return FIRST;
        }
        #endregion
    
        ...
    }
    
    0 讨论(0)
提交回复
热议问题