18

別の方法で実装列挙型に関する見栄えの良い例を見つけました。それはタイプセーフな列挙型パターンと呼ばれていると思います。使い始めましたが、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;
    }
}

C#のswitchステートメントでこのパターンを使用できるようにするためにクラスに何を追加できますか?
ありがとう。

4

3 に答える 3

9

個々の列挙型メンバー (インスタンス)に動作を追加できるため、タイプ セーフな列挙型パターンは興味深いものです。したがって、オンにしたい動作がクラスの一部である可能性がある場合は、ポリモーフィズムを使用してください。動作をオーバーライドする各メンバーのサブクラスを作成する必要がある場合があることに注意してください。

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

この場合、タイプ セーフな列挙型クラスに動作がない場合、列挙型自体の代わりに存在する理由はあまりありません。もちろん、ロジックと兄弟の列挙型を同時に持つこともできます。

于 2012-04-11T16:25:55.563 に答える
2

Jordão は正しい考えを持っていますが、ポリモーフィズムを実装するためのより良い方法があり、デリゲートを使用します。

デリゲートの使用は、switch ステートメントよりも高速です。(実際、私は、オブジェクト指向開発における switch ステートメントの唯一の場所はファクトリ メソッド内にあると強く信じています。私は、扱うコード内のすべての 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

    ...
}
于 2012-11-25T19:01:06.093 に答える