我经常看到并使用带有附加属性的枚举来做一些基本的事情,比如提供显示名称或描述:
public enum Movement {
[DisplayName("Turned Right")]
TurnedRight,
[DisplayName("Turned Left")]
[Description("Execute 90 degree turn to the left")]
TurnedLeft,
// ...
}
并且有一组扩展方法来支持这些属性:
public static string GetDisplayName(this Movement movement) { ... }
public static Movement GetNextTurn(this Movement movement, ...) { ... }
遵循此模式,可以将其他现有或自定义属性应用于字段以执行其他操作。它几乎就像枚举可以作为简单的枚举值类型一样工作,并且作为一个包含许多字段的更丰富的不可变值对象:
public class Movement
{
public int Value { get; set; } // i.e. the type backing the enum
public string DisplayName { get; set; }
public string Description { get; set; }
public Movement GetNextTurn(...) { ... }
// ...
}
通过这种方式,它可以在序列化过程中作为一个简单的字段“移动”,快速比较等等,但行为可以“内化”(ala OOP)。
那就是说,我认识到这可能被视为一种反模式。与此同时,我的一部分认为这很有用, anti 可能过于严格。
答案 0 :(得分:7)
我认为这在C#中是一个糟糕的模式,因为声明和访问属性的语言支持是如此残缺;它们并不意味着存储大量数据。用非平凡值声明属性是一件痛苦的事,获取属性的值很痛苦。一旦你想要与你的枚举相关的远程有趣的东西(比如在枚举上计算某些东西的方法,或者包含非原始数据类型的属性),你需要将它重构为一个类或者把另一个东西放在一个类中。一些带外的地方。
使用保存相同信息的静态实例制作不可变类并不是更难,在我看来,它更具惯用性。
答案 1 :(得分:5)
我会说这是一种反模式。这就是原因。让我们把你现有的枚举(在这里简化剥离属性):
public enum Movement
{
TurnedRight,
TurnedLeft,
Stopped,
Started
}
现在,让我们说需要扩展到更精确的东西;比方说,标题和/或速度的变化,将“伪类”中的一个“字段”变为两个:
public sealed class Movement
{
double HeadingDelta { get; private set; }
double VelocityDelta { get; private set; }
// other methods here
}
所以,你有一个编码的枚举,现在必须转换成一个不可变的类,因为你现在跟踪两个正交(但仍然是不可变的)属性,这些属性确实属于同一个接口。你写的针对“丰富的枚举”的任何代码现在必须被彻底删除和重写;然而,如果你开始作为一个班级,你可能会做的工作较少。
你必须问一下如何在一段时间内维护代码,以及丰富的枚举是否比类更易于维护。我敢打赌,它不会更易于维护。此外,正如mquander所指出的那样,基于类的方法在C#中更为惯用。
还有其他需要考虑的事项。如果对象是不可变的并且是struct
而不是class
,那么您将获得相同的值传递语义,并且序列化大小和对象的运行时大小可以忽略不计枚举。