13

一位开发人员最近开始在通常适合枚举的地方使用类模式而不是枚举。相反,他使用了类似于下面的东西:

internal class Suit
{
    public static readonly Suit Hearts = new Suit();
    public static readonly Suit Diamonds = new Suit();
    public static readonly Suit Spades = new Suit();
    public static readonly Suit Clubs = new Suit();
    public static readonly Suit Joker = new Suit();
    private static Suit()
    {

    }
    public static bool IsMatch(Suit lhs, Suit rhs)
    {
        return lhs.Equals(rhs) || (lhs.Equals(Joker) || rhs.Equals(Joker));
    }
}

他的理由是它看起来像一个枚举,但允许他包含与枚举相关的方法(如上面的 IsMatch),以包含在枚举本身中。

他称这是一个枚举类,但它不是我以前见过的。我想知道优点和缺点是什么,在哪里可以找到更多信息?

谢谢

编辑:他描述的另一个优点是能够为枚举添加特定的 ToString() 实现。

4

3 回答 3

7

枚举在很多情况下都很好,但在其他情况下却很差。通常我会发现 Enums 存在一些问题:

  • 与枚举相关的行为分散在应用程序中
  • 新的枚举值需要霰弹枪手术
  • 枚举不遵​​循开闭原则

由于枚举行为分散在各处,我们永远无法将其带回源类型,因为枚举类型不能有任何行为(或就此而言的状态)。

另一方面,使用枚举类

每个枚举类型的所有变体不仅可以下推到枚举类,还可以下推到每个特定的子类型。

枚举在各种场景中都能很好地工作,但可能会在您的域模型中快速分解。枚举类提供了许多相同的可用性,并具有成为行为目标的额外好处。

不再需要切换语句,因为我可以将可变性和知识推回模型中。如果由于某种原因我需要检查特定的枚举类值,该选项仍然对我开放。这种模式不应该取代所有的枚举,但有一个替代方案很好。

可以在这里阅读

于 2013-05-12T13:26:19.983 回答
4

枚举的主要优点是它们基本上是带有一些命名值的整数,因此它们本质上是可移植和可序列化的。算术和逻辑运算在枚举上也更快。

当您需要具有额外状态信息的不透明值时,使用枚举类。例如,通用数据访问层可以具有如下接口:

公共静态类 Dal
{
    公共静态记录查询(操作操作,参数参数);
}

var result = Dal.Query(Operation.DoSomething, new DoSomethingParameters {...});

对于 Dal 操作的用户来说,操作只是可用操作的枚举,但它可以包含连接字符串、SQL 语句或存储过程以及通用 Dal 所需的任何其他数据。

另一个“常见”用途是系统(状态或策略)中的公共模式。从用户的角度来看,模式是一个不透明的值,但它可能包含对系统实施至关重要的信息或内部功能。一个人为的例子:

公共类 TheSystem
{
   公共系统模式模式;
   公共无效保存()
   {
      模式.保存();
   }
   公共系统文档读取()
   {
      返回模式.Read();
   }
}

公共抽象类 SystemMode
{
   public static SystemMode ReadOnly = new ReadOnlyMode();
   公共静态 SystemMode ReadWrite = new ReadWriteMode();

   内部抽象 void Save();
   内部抽象 SystemDocument Read();

   私有类 ReadOnlyMode : SystemMode
   {
      内部覆盖 void Save() {...}
      内部覆盖 SystemDocument Read() {...}
   }

   私有类 ReadWriteMode : SystemMode
   {
      内部覆盖 void Save() {...}
      内部覆盖 SystemDocument Read() {...}
   }
}


TheSystem.Mode = SystemMode.ReadOnly;

我不认为仅仅拥有一个 IsMatch 静态方法就可以保证不使用简单的枚举。在这种情况下,使用扩展方法可以实现非常相似的效果。

于 2013-05-12T13:10:12.137 回答
3

枚举非常适合轻量级状态信息。例如,您的颜色枚举(不包括蓝色)非常适合查询交通灯的状态。真正的颜色以及颜色的整个概念及其所有包袱(alpha、颜色空间等)并不重要,光处于哪个状态无关紧要。另外,稍微改变你的枚举来表示交通灯的状态:

[Flags()]
public enum LightColors
{
    unknown = 0,
    red = 1,
    yellow = 2,
    green = 4,
    green_arrow = 8
}

当前的灯光状态可以设置为:

LightColors c = LightColors.red | LightColors.green_arrow;

并查询为:

if ((c & LightColors.red) == LightColors.red)
    {
        //Don't drive
    }
    else if ((c & LightColors.green_arrow) == LightColors.green_arrow)
    {
        //Turn
    }

静态类颜色成员将能够支持这种多状态而无需额外的功能。

然而,静态类成员对于常用对象来说非常棒。System.Drawing.Color 成员就是很好的例子,因为它们代表具有晦涩构造函数的已知颜色(除非您知道十六进制颜色)。如果它们被实现为枚举,那么每次您想将值用作颜色时都必须执行以下操作:

colors c = colors.red;
        switch (c)
        {
            case colors.red:
                return System.Drawing.Color.FromArgb(255, 0, 0);
                break;
            case colors.green:
                return System.Drawing.Color.FromArgb(0,255,0);
                break;
        }

因此,如果您有一个枚举并发现您不断地执行 switch/case/if/else/whatever 来派生一个对象,那么您可能想要使用静态类成员。如果您只是查询某物的状态,我会坚持使用枚举。此外,如果您必须以不安全的方式传递数据,枚举可能会比您的对象的序列化版本更好地生存。

参考来自该论坛的旧帖子: 何时使用枚举,以及何时将它们替换为具有静态成员的类?

于 2013-05-12T13:21:08.473 回答