Как я могу использовать оператор switch для типа перечислимого типа
Я нашел хороший пример о перечислениях реализации по-другому. Думаю, это называется типом перечисления типа. Я начал использовать его, но я понял, что не могу использовать его в инструкции switch.
Моя реализация выглядит следующим образом:
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;
}
}
Что я могу добавить в мой класс, чтобы иметь возможность использовать этот шаблон в операторах switch на С#?
Спасибо.
Ответы
Ответ 1
Вы можете попробовать что-то вроде этого:
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;
}
}
Ответ 2
Типоразмерный шаблон перечисления интересен тем, что вы можете добавлять поведение к отдельным членам перечисления (которые являются экземплярами). Итак, если поведение, которое вы хотите включить, может быть частью класса, просто используйте полиморфизм. Обратите внимание, что вам может потребоваться создать подклассы для каждого члена, который переопределяет поведение:
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-type enum не имеет никакого поведения, у него нет большой причины существовать вместо самого перечисления. Но, конечно же, вы можете одновременно иметь логику и переписку между сестрами.
Ответ 3
У Jordão есть правильная идея, но есть лучший способ реализовать полиморфизм, использовать делегат.
Использование делегатов выполняется быстрее, чем оператор switch. (На самом деле я твердо убежден, что единственное место для операторов switch в объектно-ориентированном развитии - это метод factory. Я всегда ищу какой-то полиморфизм для замены любых операторов switch в любом коде, к которому я обращаюсь.)
Например, если вам нужно определенное поведение, основанное на типе-безопасном перечислении, следующий шаблон использует то, что я использую:
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
...
}