使用枚举类来代替枚举类型

Enumerations (or enum types for short) are a thin language wrapper around an integral type. You might want to limit their use to when you are storing one value from a closed set of values. Classification based on sizes (small, medium, large) is a good example. Using enums for control flow or more robust abstractions can be a code smell. This type of usage leads to fragile code with many control flow statements checking values of the enum.

枚举(或简称为枚举类型)本质上只是基于整型类型的一层薄薄的语言包装。你可能需要将它们的使用场景限制在:仅用于存储一个封闭值集合中的某一个值。比如基于尺寸的分类(小、中、大)就是一个很好的例子。如果把枚举用于控制流或者更健壮的抽象,可能会成为一种“代码坏味道”。这种用法会导致代码变得非常脆弱,因为会出现大量针对枚举值进行检查的控制流语句。

Instead, you can create Enumeration classes that enable all the rich features of an object-oriented language.

取而代之的是,你可以创建“枚举类”,这样就能充分利用面向对象编程语言的所有丰富特性。

However, this isn't a critical topic and in many cases, for simplicity, you can still use regular enum types if that's your preference. The use of enumeration classes is more related to business-related concepts.

不过,这并不是一个绝对的关键话题。在很多情况下,如果你觉得方便,依然可以使用常规的枚举类型。使用枚举类更多地是与业务相关的概念挂钩。

Implement an Enumeration base class    实现一个枚举基类

The ordering microservice in eShopOnContainers provides a sample Enumeration base class implementation, as shown in the following example:

eShopOnContainers 的订单微服务提供了一个枚举基类的实现示例,如下所示:

/// <summary>
/// 强类型枚举类的抽象基类。
/// 在 DDD 中用于替代传统的 C# enum,以支持更丰富的面向对象特性(如添加方法、属性及状态控制流)。
/// </summary>
public abstract class Enumeration : IComparable
{
    /// <summary>
    /// 获取枚举项的名称。
    /// </summary>
    public string Name { get; private set; }

    /// <summary>
    /// 获取枚举项的唯一标识符 ID。
    /// </summary>
    public int Id { get; private set; }

    /// <summary>
    /// 受保护的构造函数,仅允许派生类初始化枚举实例的 ID 和名称。
    /// </summary>
    /// <param name="id">枚举项的唯一标识。</param>
    /// <param name="name">枚举项的名称。</param>
    protected Enumeration(int id, string name) => (Id, Name) = (id, name);

    /// <summary>
    /// 重写 ToString 方法,直接返回枚举项的名称以便于阅读和日志记录。
    /// </summary>
    /// <returns>枚举项的名称字符串。</returns>
    public override string ToString() => Name;

    /// <summary>
    /// 通过反射获取指定枚举类 T 中定义的所有公共静态字段,并作为 IEnumerable 集合返回。
    /// 常用于遍历该枚举类下的所有可用选项。
    /// </summary>
    /// <typeparam name="T">继承自 Enumeration 的具体枚举类。</typeparam>
    /// <returns>包含所有静态枚举实例的可枚举集合。</returns>
    public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
        typeof(T).GetFields(BindingFlags.Public |       // 仅查找公共字段
                            BindingFlags.Static |       // 仅查找静态字段
                            BindingFlags.DeclaredOnly)  // 仅查找当前类声明的字段,排除继承的字段
                 .Select(f => f.GetValue(null))         // 获取静态字段的值(无需实例)
                 .Cast<T>();                            // 将结果强制转换为具体的枚举类型 T

    /// <summary>
    /// 重写 Equals 方法,基于对象的实际类型和 ID 进行相等性比较,确保同类型且同 ID 的枚举被视为相等。
    /// </summary>
    /// <param name="obj">要比较的对象。</param>
    /// <returns>如果类型和 ID 均匹配则返回 true,否则返回 false。</returns>
    public override bool Equals(object obj)
    {
        // 使用模式匹配检查传入对象是否为 Enumeration 类型,若失败则直接返回 false
        if (obj is not Enumeration otherValue)
        {
            return false;
        }

        // 检查两个对象的运行时类型是否完全一致(防止不同枚举类之间的误判)
        var typeMatches = GetType().Equals(obj.GetType());
        
        // 检查两个对象的 ID 是否相等
        var valueMatches = Id.Equals(otherValue.Id);

        // 只有当类型和 ID 都匹配时才认为两者相等
        return typeMatches && valueMatches;
    }

    /// <summary>
    /// 实现 IComparable 接口,基于 ID 的大小对枚举实例进行排序比较。
    /// </summary>
    /// <param name="other">要比较的另一个对象。</param>
    /// <returns>比较结果整数(小于0、等于0或大于0)。</returns>
    public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);

    // ...其他实用工具方法...
}

You can use this class as a type in any entity or value object, as for the following CardType : Enumeration class:

你可以将这个类作为任何实体或值对象的类型来使用,比如下面的 CardType(卡片类型)枚举类:

/// <summary>
/// 支付卡类型枚举类。
/// 继承自 Enumeration 基类,以强类型的对象形式表示各种支持的卡片类型,而非使用传统的整数枚举。
/// </summary>
public class CardType : Enumeration
{
    /// <summary>
    /// 美国运通卡(American Express)。
    /// </summary>
    public static CardType Amex = new(1, nameof(Amex)); // 定义静态实例:ID 为 1,名称为 "Amex"

    /// <summary>
    /// Visa 信用卡/借记卡。
    /// </summary>
    public static CardType Visa = new(2, nameof(Visa)); // 定义静态实例:ID 为 2,名称为 "Visa"

    /// <summary>
    /// 万事达卡(MasterCard)。
    /// </summary>
    public static CardType MasterCard = new(3, nameof(MasterCard)); // 定义静态实例:ID 为 3,名称为 "MasterCard"

    /// <summary>
    /// 初始化一个新的 <see cref="CardType"/> 实例。
    /// </summary>
    /// <param name="id">卡类型的唯一数字标识符,用于数据库持久化。</param>
    /// <param name="name">卡类型的字符串名称。</param>
    public CardType(int id, string name)
        : base(id, name) // 将 ID 和 Name 传递给抽象基类进行统一管理
    {
    }
}

 

posted @ 2026-05-17 18:58  菜鸟吊思  阅读(9)  评论(0)    收藏  举报