1

当初、私の望みは列挙型で何らかのカプセル化を行うことでしたが、C#ではまだそれが不可能なようです。私の目的は、列挙型を使用してある種のデータを格納することでした。理想的には、オブジェクトの列挙型があれば素晴らしいと思います。しかし、どうやらそれを行う方法はありません。そこで、状態クラス、パブリック列挙型を作成し、オブジェクトをプロパティとして設定できるメソッドを初期化するセッターを使用して、パブリックゲッター/セッターを作成しました。もう少し良い方法があるのだろうかと思います。私にとって理想的な解決策は、通常の方法で状態(列挙型)を設定することです。

car.State = CarStates.Idle;

次に、その状態に関するより多くのデータに次のようにアクセスします。

string message = car.State.Message();

ただし、これには、プロパティをState列挙型にアタッチすることが含まれます。この効果を達成するためのクールなトリックはありますか?列挙型は、末尾に追加するだけで切り替え可能なシングルトンのような値を作成できる唯一の方法ですか?.Idle

これが私が今持っているコードで、レイヤーを追加することで状態情報を1つのレベルに保ちます。これは無難ですが、のようなものを宣言すると冗長に感じますcar.Mode.State = CarModeStates.Idle;

class Car
{
    public CarState Mode;

    public Car()
    {
        Mode = new CarState();
    }
}

class CarState //holds any metadata for given enum value
{
    private int _EnumInt;
    private string _Message;
    private bool _IsError = false;

    public CarModeStates State
    { 
        get { return (CarModeStates)_EnumInt; } 
        set { _EnumInt = (int)value; InitializeState(value); } 
    }
    public string Message { get { return _Message; } }
    public bool IsError { get { return _IsError; } }

    public void InitializeState(CarModeStates? cs)
    {
        switch (cs)
        {
            case (CarModeStates.Off):
                {
                    _Message = "Car is off.";
                    _IsError = false;
                    break;
                }
            case (CarModeStates.Idle):
                {
                    _Message = "Car is idling.";
                    _IsError = false;
                    break;
                }
            case (CarModeStates.Drive):
                {
                    _Message = "Car is driving.";
                    _IsError = false;
                    break;
                }
            case (CarModeStates.Accident):
                {
                    _Message = "CRASH!";
                    _IsError = true;
                    break;
                }
        }
    }
}

public enum CarModeStates
{
    Off,
    Idle,
    Drive,
    Accident,
}

//And from the outside:
class SomeController
{
    Car car = new Car();
    public string GetStateMessage()
    {
        car.Mode.State = CarModeStates.Idle;

        return car.Mode.Message;
    }
}
4

6 に答える 6

4

拡張メソッドを試しましたか?静的クラスで、次を定義します。

public static string Message( this CarStates value )
{
    switch (value) {
    ...
    }
}
于 2013-03-10T07:03:11.060 に答える
3

C#の通常の列挙型は、多くの場合、質問が示唆するほど問題にはなりません。有効な値の1つを取得していることを確認するために、switchステートメントdefaultにがをスローするケースがあることを確認してください。ArgumentExceptionリテラル値(暗黙的に任意の列挙型に変換可能)に注意する必要がありますが0、それ以外の場合enum、C#のinはAPIレベルで実質的な型の安全性を提供します。

C#列挙型は、Javaで使用されるような完全にカプセル化された列挙型よりも大幅に優れたパフォーマンスを発揮します。注意すべき追加の詳細がいくつかありますが(たとえば、値が範囲外)、実際に問題が発生することはめったにありません。


Javaスタイルの列挙型クラスはC#で簡単に作成できます。編集:このような列挙型のC#バージョンでステートメントを使用できないという事実を除いて。switch

  1. クラスを作成しsealedます。
  2. クラスに少なくとも1つの明示的なコンストラクターがあることを確認し、すべてのコンストラクターがであることを確認してprivateください。
  3. 次のようにメンバーを翻訳します。

Java:

enum Color { RED, GREEN, BLUE }

C#:

private class Color {
    public static readonly Color RED = new Color();
    public static readonly Color GREEN = new Color();
    public static readonly Color BLUE = new Color();

    private Color() { }
}

本当にJavaを模倣したい場合は、プロパティEnum<T>を維持するこのフォームの列挙型の抽象基本クラスを作成し、クラスに次のようなOrdinal静的プロパティを作成することで、賢くなります。ValuesColor

public Color[] Values { get { return new[] { RED, GREEN, BLUE }; } }
于 2013-03-10T06:45:01.203 に答える
1

値だけを使用したい場合はEnum、独自の属性を作成して追加のメタデータを提供できます。

以下の例は機能しますが、私は個人的にこの方法でドメインをモデル化することはしません。

// sample test app
class Program
{
    static void Main(string[] args)
    {
        var carState = CarModeStates.Accident;

        // the call to get the meta data could and probably should be stored in a local variable
        Console.WriteLine(carState.GetMetaData().Message);
        Console.WriteLine(carState.GetMetaData().IsError);
        Console.WriteLine(carState.GetMetaData().IsUsingPetrol);
        Console.Read();
    }
}

拡張列挙型の例

// enum with meta data
public enum CarModeStates
{
    [CarStatus("Car is off."), IsError(false), IsUsingPetrol(false)]
    Off,

    [CarStatus("Car is idling."), IsError(false), IsUsingPetrol(true)]
    Idle,

    [CarStatus("Car is driving."), IsError(false), IsUsingPetrol(true)]
    Drive,

    [CarStatus("CRASH!"), IsError(true), IsUsingPetrol(false)]
    Accident
}

列挙型を装飾するためのカスタム属性

public interface IAttribute<out T>
{
    T Description { get; }
}

[AttributeUsage(AttributeTargets.Field)]
public class CarStatusAttribute : Attribute, IAttribute<string>
{
    private readonly string _value;

    public CarStatusAttribute(string value)
    {
        _value = value;
    }

    public string Description
    {
        get { return _value; }
    }
}

[AttributeUsage(AttributeTargets.Field)]
public class IsErrorAttribute : Attribute, IAttribute<bool>
{
    private readonly bool _value;

    public IsErrorAttribute(bool value)
    {
        _value = value;
    }

    public bool Description
    {
        get { return _value; }
    }
}

[AttributeUsage(AttributeTargets.Field)]
public class IsUsingPetrolAttribute : Attribute, IAttribute<bool>
{
    private readonly bool _value;

    public IsUsingPetrolAttribute(bool value)
    {
        _value = value;
    }

    public bool Description
    {
        get { return _value; }
    }
}

列挙型に関するメタデータを取得するための拡張メソッド。

public static class CarModeStatesExtensions
{
    public static CarModeStateModel GetMetaData(this CarModeStates value)
    {
        var model = new CarModeStateModel
            {
                Message = value.GetDescriptionFromEnumValue<string>(typeof (CarStatusAttribute)),
                IsError = value.GetDescriptionFromEnumValue<bool>(typeof(IsErrorAttribute)),
                IsUsingPetrol = value.GetDescriptionFromEnumValue<bool>(typeof (IsUsingPetrolAttribute))
            };

        return model;
    }
}

public class CarModeStateModel
{
    public string Message { get; set; }
    public bool IsError { get; set; }
    public bool IsUsingPetrol { get; set; }
}

public static class EnumExtensions
{
    public static T GetDescriptionFromEnumValue<T>(this CarModeStates value, Type attributeType)
    {
        var attribute = value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(attributeType, false).SingleOrDefault();

        if (attribute == null)
        {
            return default(T);
        }

        return ((IAttribute<T>)attribute).Description;
    }
}
于 2013-03-10T07:28:08.120 に答える
1

辞書と組み合わせた拡張メソッドは、switchステートメントの代わりに解決策になる可能性があります。

public static class CarExtensionMethods
{
    public static string Message(this CarStates value)
    {
        return carStateDictionary[value];
    }

    private static readonly Dictionary<CarStates, string> carStateDictionary;

    static CarExtensionMethods()
    {
        carStateDictionary = new Dictionary<CarStates, string>();

        carStateDictionary.Add(CarStates.Off, "Car is off.");
        carStateDictionary.Add(CarStates.Idle, "Car is idling.");
        carStateDictionary.Add(CarStates.Drive, "Car is driving.");
        carStateDictionary.Add(CarStates.Accident, "CRASH!");
    }
}

使用法は非常に簡単です。

CarStates state = CarState.Idle;
Console.WriteLine(state.Message());  //writes "Car is idling."

また、補足として、通常、enum名前には属性がある場合にのみ複数形にする必要があり[Flags]ます。重要なのは、それらが複数の状態を持つことができることを示すことです。列挙型のよりスタイル的に適切な名前はCarState、一度に複数の状態を持つことができないためです。

于 2013-03-10T07:20:26.900 に答える
1

多分あなたはそのような「工場」を必要とします:

public class CarState
{
    private string message;
    private bool error;

    public CarState(string message, bool error)
    {
        this.message = message;
        this.error = error;
    }

    public string Message
    {
        get { return this.message; }
    }

    public bool Error
    { 
        get { return this.error; }
    }
}

public static class CarStateFactory
{
    public enum CarStateId { Off, Idle, Driving, Accident }

    public static CarState GetCarState(CarStateId carStateId)
    {
        switch(carStateId)
        {
            case (CarStateId.Off):
                { return new CarState("Car is off", false); }

            //add more cases

            default:
                return null;
        }
    }
}

これを使用すると、電話で車の状態を設定できます。

car.State = CarStateFactory.GetCarState(CarStateFactory.ID.Off); //ID.Idle, ID.Driving, ID.Accident

でメッセージにアクセスできますcar.State.Message

編集:CarStateFactoryの静的ゲッター

public static CarState Idle
{
    get
    {
        return new CarState("Car is off", false);
    }
}
于 2013-03-10T07:58:44.943 に答える
0

これはかなり古いことは知っていますが、今後の訪問者には、列挙型を使用するのではなく、オブジェクトが各状態を表す状態マネージャーを使用する可能性がありますか?

概要

/// <summary>
/// Defines the expected members of a state
/// </summary>
internal interface ICarState
{
    /// <summary>
    /// Gets or sets the message.
    /// </summary>
    /// <value>
    /// The message.
    /// </value>
    string Message { get; }
}

基本状態

/// <summary>
/// The class that all car states should inherit from.
/// </summary>
internal abstract class CarBaseState : ICarState
{
    #region ICarState Members

    /// <summary>
    /// Gets or sets the message.
    /// </summary>
    /// <value>
    /// The message.
    /// </value>
    /// </exception>
    public abstract string Message { get; }

    #endregion
}

実装

/// <summary>
/// Represents the state when the car is off
/// </summary>
internal class OffState : CarBaseState
{
    /// <summary>
    /// Gets or sets the message.
    /// </summary>
    /// <value>
    /// The message.
    /// </value>
    /// </exception>
    public override string Message { get { return "Off"; } }
}

/// <summary>
/// Represents the state when the car is idling
/// </summary>
internal class IdleState : CarBaseState
{
    /// <summary>
    /// Gets or sets the message.
    /// </summary>
    /// <value>
    /// The message.
    /// </value>
    /// </exception>
    public override string Message { get { return "Idling"; } }
}

マネジャー

internal class CarStateManager
{
    #region Fields

    Dictionary<string, ICarState> _stateStore = null;

    #endregion

    #region Properties

    /// <summary>
    /// Gets (or privately sets) the state of the current.
    /// </summary>
    /// <value>
    /// The state of the current.
    /// </value>
    internal ICarState CurrentState { get; private set;  }

    #endregion

    #region Constructors and Initialisation

    /// <summary>
    /// Initializes a new instance of the <see cref="StateManager"/> class.
    /// </summary>
    public CarStateManager()
    {
        _stateStore = new Dictionary<string, ICarState>();
    }

    #endregion

    #region Methods

    /// <summary>
    /// Adds a state.
    /// </summary>
    /// <param name="stateId">The state identifier.</param>
    /// <param name="state">The state.</param>
    public void AddState(string stateId, ICarState state)
    {
        // Add the state to the collection
        _stateStore.Add(stateId, state);
    }

    /// <summary>
    /// Changes the state.
    /// </summary>
    /// <param name="stateId">The state identifier.</param>
    public void ChangeState(string stateId)
    {

        // Set thr current state
        CurrentState = _stateStore[stateId];
    }

    #endregion
}

プログラム

class Program
{
    internal class StateKeys
    {
        public const string Off = "Off";
        public const string Idle = "Idle";
    }

    static void Main(string[] args)
    {
        // Instantiate the state manager
        CarStateManager stateManager = new CarStateManager();

        // Add the states
        stateManager.AddState(StateKeys.Off, new OffState());
        stateManager.AddState(StateKeys.Idle, new IdleState());

        // Change the state and display the message
        stateManager.ChangeState(StateKeys.Off);
        Console.WriteLine(stateManager.CurrentState.Message);

        // Change the state and display the message
        stateManager.ChangeState(StateKeys.Idle);
        Console.WriteLine(stateManager.CurrentState.Message);
        Console.ReadLine();
    }
}

出力:

Off
Idling

HTH!

于 2015-01-19T07:49:39.477 に答える