Вопрос по c# – как я могу использовать оператор switch на типе enum-safe

16

Я нашел хороший пример реализации перечислений по-другому. Это называетсяtype-safe enum pattern Я думаю. Я начал использовать его, но понял, что не могу использовать его в операторе switch.
Моя реализация выглядит следующим образом:

<code>public sealed class MyState
{
    private readonly string m_Name;
    private readonly int m_Value;

    public static readonly MyState PASSED= new MyState(1, "OK");
    public static readonly MyState FAILED= new MyState(2, "ERROR");

    private MyState(int value, string name)
    {
        m_Name = name;
        m_Value = value;
    }

    public override string ToString()
    {
        return m_Name;
    }

    public int GetIntValue()
    {
        return m_Value;
    }
}
</code>

Что я могу добавить в свой класс, чтобы иметь возможность использовать этот шаблон вswitch заявления в C #?
Благодарю.

@ Если использование вашего класса вместо перечислений абсолютно правильно. Я никогда не использую перечисления в C # по многим причинам. Alex Burtsev
Согласитесь с @CodyGray, весь смысл перечислений в безопасности типов. harpo
GetIntValue должен быть свойствомValue или жеID, Вы можете поменять это свойство Gilad Naaman
@CodyGray обратитесь к этому вопросу:stackoverflow.com/questions/424366/c-sharp-string-enums , Я взял реализацию из ответа на этот вопрос. И я подумал, что это как стандартный шаблон дизайна для enums. Ну, я думаю, что мне придется подумать о другом способе достижения своей цели. Благодарю. Fer
Что недостаточно «типобезопасно» о стандартной реализации enum? Cody Gray♦

Ваш Ответ

3   ответа
9

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;
    }
}
Error: User Rate Limit ExceededswitchError: User Rate Limit Exceeded
7

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

  ...
}

Использование:

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

Теперь, если логика явно не принадлежит, и вы действительно хотите переключиться, я советую создать перечисление сиблинга:

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) {
    ...
  }
}

И включите это:

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

В этом случае, если класс enum с типобезопасным типом не имеет какого-либо поведения, у него нет особых оснований существовать вместо самого перечисления. Но, конечно, вы можете иметь логику и перечисление родного брата одновременно.

Error: User Rate Limit Exceeded Fer
1

o правильная идея, но есть лучший способ реализовать полиморфизм, используйтеdelegate.

Использование делегатов быстрее, чем оператор switch. (На самом деле я твердо убежден, что единственное место для операторов switch в объектно-ориентированной разработке - это метод фабрики. Я всегда ищу какой-то полиморфизм для замены любых операторов switch в любом коде, с которым я имею дело.)

Например, если вы хотите конкретное поведение, основанное на типе-enum-безопасности, я использую следующий шаблон:

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

    ...
}

Похожие вопросы