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