处理枚举更改方法的更好方法

本文关键字:方法 更好 枚举 处理 | 更新日期: 2023-09-27 18:03:22

最初,我希望对枚举进行某种封装,但显然这在c#中还不可能。我的目的是用枚举存储某种类型的数据。理想情况下,拥有一个对象枚举是非常好的。但显然没有办法做到这一点。因此,我创建了一个状态类,一个公共枚举,并创建了一个公共getter/setter,其中setter初始化了一个方法,我可以在其中将对象填充为属性。我在想有没有更好的办法。对我来说,理想的解决方案是按正常方式设置状态(enum):

car.State = CarStates.Idle;

,然后访问关于该状态的更多数据,如下所示:

string message = car.State.Message();

但是这将涉及到给State枚举附加一个属性。有什么很酷的技巧可以达到这种效果吗?枚举是您通过简单地将.Idle添加到末尾而使可切换的类单例值的唯一方法吗?

这是我现在所拥有的代码,通过添加一个层将状态信息保持在一个级别,这是可以通过的,但是在声明像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;
    }
}

处理枚举更改方法的更好方法

您尝试过扩展方法吗?在静态类中,定义:

public static string Message( this CarStates value )
{
    switch (value) {
    ...
    }
}

c#中的常规枚举通常不像你的问题所暗示的那样有问题。只要确保你的switch语句有一个抛出ArgumentExceptiondefault情况,以确保你得到一个有效值。您必须小心字面值0(它可以隐式转换为任何枚举类型),但除此之外,c#中的enum确实在API级别提供了实质性的类型安全性。

c#中

枚举的性能明显优于Java中完全封装的枚举。还有一些额外的细节需要注意(例如,值超出范围),但它们在实践中很少引起问题。


java风格的枚举类很容易在c#中创建。编辑:除了你不能在c#版本的枚举上使用switch语句。
  1. 创建sealed
  2. 确保类至少有一个显式构造函数,并确保所有构造函数都是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属性,并在Color类中创建一个静态的Values属性,如下所示:

public Color[] Values { get { return new[] { RED, GREEN, BLUE }; } }

与字典相结合的扩展方法可能是替代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]属性。关键是表明它们可以有多个状态。一个更适合你的enum的名字是CarState,因为它不能同时有多个状态。

如果您想只是使用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示例

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

装饰enum的自定义属性

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

也许你需要一个这样的"factory":

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的静态getter

public static CarState Idle
{
    get
    {
        return new CarState("Car is off", false);
    }
}

我知道这是相当旧的,但对于进一步的访问者,而不是使用枚举,也许使用一个状态管理器,其中一个对象代表每个状态?

抽象

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