C# 中的“智能枚举”:如何在枚举中增加行为

科技资讯 投稿 8200 0 评论

C# 中的“智能枚举”:如何在枚举中增加行为

目录

    枚举的基本用法回顾
  • 枚举常见的设计模式运用
    • 介绍
  • 智能枚举
      代码示例
    • 业务应用
  • 小结

枚举的基本用法回顾

C# 枚举(enum)的示例:

enum Weekday
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}
class Program
{
    static void Main(string[] args
    {
        Weekday today = Weekday.Tuesday;

        Console.WriteLine("今天是" + today.ToString( + "。";
        Console.WriteLine("明天是" + (Weekday(((inttoday + 1 % 7 + "。";
        Console.WriteLine("昨天是" + (Weekday(((inttoday + 6 % 7 + "。";
    }
}

在这个示例中,我们定义了一个名为 Weekday 的枚举,其中包括每个星期的日子。然后在 Main 方法中,我们将 today 变量设置为 Tuesday,并使用 ToString( 方法将其转换为字符串。

输出结果应该是这样的:

今天是 Tuesday。
明天是 Wednesday。
昨天是 Monday。

枚举常见的设计模式运用

enum 可以应用在许多种设计模式下:

    状态模式
  • 策略模式
  • 工厂模式
  • 观察者模式

介绍

    状态模式

enum 可以很好地表示对象的状态,因此它是实现状态模式的常见选择。在 C# 中,您可以使用 switch 语句来根据不同的 enum 值执行不同的操作。

  1. 策略模式

enum 可以很好地表示这些条件,因此它是实现策略模式的常见选择。在 C# 中,您可以使用 switch 语句或 if-else 语句来根据不同的 enum 值选择不同的算法或行为。

  1. 工厂模式

enum 可以很好地表示这些对象的类型,因此它是实现工厂模式的常见选择。在 C# 中,您可以使用 switch 语句或 if-else 语句来根据不同的 enum 值创建不同的对象。

  1. 观察者模式

enum 可以很好地表示观察者对象的状态,因此它是实现观察者模式的常见选择。在 C# 中,您可以使用 enum 来表示观察者对象的状态,并使用委托或事件来通知观察者对象。

智能枚举

智能枚举不是官方的一个称谓,而是作者定义的一个名词。

智能枚举 = 枚举 + 丰富的行为。

enum 类型(值类型)改变成了 class 类型(引用类型),允许您将行为和方法绑定到每个枚举类型上。这意味着您可以在枚举类型上调用方法和属性,就像在类实例上调用它们一样。

代码示例

首先,我们先定义一个抽象的类型,方便后续重用:

public abstract class Enumeration<TEnum> : IEquatable<Enumeration<TEnum>> where TEnum : Enumeration<TEnum>
{
    private static readonly Dictionary<int, TEnum?> Enumerations = GetEnumerations(;

    /// <summary>
    /// 枚举类型
    /// </summary>
    private static readonly Type EnumType = typeof(TEnum;

    protected Enumeration(int value, string name
    {
        Value = value;
        Name = name;
    }

    /// <summary>
    /// 名称
    /// </summary>
    public string Name { get; protected init; }

    /// <summary>
    /// 值
    /// </summary>
    public int Value { get; protected init; }

    public static TEnum? FromName(string name => Enumerations.Values.SingleOrDefault(x => x.Name == name;

    public static TEnum? FromValue(int value => Enumerations.TryGetValue(value, out var enumeration ? enumeration : default;

    public bool Equals(Enumeration<TEnum>? other => other is not null && GetType( == other.GetType( && Value == other.Value;

    public override bool Equals(object? obj => obj is Enumeration<TEnum> other && Equals(other;

    public override int GetHashCode( => HashCode.Combine(Value, Name;

    private static Dictionary<int, TEnum?> GetEnumerations( => EnumType
        .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy
        .Where(x => EnumType.IsAssignableFrom(x.FieldType
        .Select(x => (TEnum?x.GetValue(default.ToDictionary(x => x?.Value ?? 0;
}

先简单解读一下。

C# 抽象类,用于实现枚举的高级功能。它使用泛型类型 TEnum 来表示枚举类型,并继承自 IEquatable<Enumeration<TEnum>> 接口,以支持比较操作。

FromName 和 FromValue,它们可以通过名称或值来获取枚举值。它还重写了 EqualsGetHashCode 方法,以便可以比较两个枚举值是否相等。

GetEnumerations,它使用反射获取当前枚举类型中的所有字段,并将它们转换为枚举值。在这个过程中,它还会检查字段的类型是否与枚举类型相同,并将值存储在一个字典中,以便以后可以快速地访问它们。

业务应用

我们通常会将枚举类型这样定义,而在触发业务逻辑时会使用 switch 来执行不同的行为,这样就很容易会将逻辑分散在不同的地方。

/// <summary>
/// 信用卡类型
/// </summary>
public enum CreditCardType
{
    None = 0,
    Standard = 1,
    Silver = 2,
    Gold = 3,
}

那么升级后的智能枚举应该是怎样的呢?它到底智能在哪里?

/// <summary>
/// 信用卡
/// </summary>
public abstract class CreditCard : Enumeration<CreditCard>
{
    public static readonly CreditCard Gold = new GoldCreditCard(;
    public static readonly CreditCard Silver = new SilverCreditCard(;
    public static readonly CreditCard Standard = new StandardCreditCard(;
    public static readonly CreditCard None = new NoneCreditCard(;
    
    private CreditCard(int value, string name : base(value, name
    {
    }

    /// <summary>
    /// 折扣
    /// </summary>
    public abstract double Discount { get; }

    /// <summary>
    /// 金卡
    /// </summary>
    private sealed class GoldCreditCard : CreditCard
    {
        public GoldCreditCard( : base(3, nameof(Gold
        {
        }

        public override double Discount => 0.2;
    }

    private sealed class SilverCreditCard : CreditCard
    {
        public SilverCreditCard( : base(2, nameof(Silver
        {
        }

        public override double Discount => 0.1;
    }

    /// <summary>
    /// 标准
    /// </summary>
    private sealed class StandardCreditCard : CreditCard
    {
        public StandardCreditCard( : base(1, nameof(Standard
        {
        }

        public override double Discount => 0.05;
    }

    /// <summary>
    /// 无
    /// </summary>
    private sealed class NoneCreditCard : CreditCard
    {
        public NoneCreditCard( : base(0, nameof(None
        {
        }

        public override double Discount => 0;
    }
}

这里简单解读一下。

Enumeration。其中,GoldCreditCardSilverCreditCardStandardCreditCardNoneCreditCard 是四个具体的信用卡子类。

CreditCard 中的 Discount 属性,以表示不同信用卡的折扣率。GoldCreditCard 有最高的折扣率,NoneCreditCard 没有任何折扣。

CreditCard 类中,GoldSilverStandardNone 是四个静态实例,表示四种不同的信用卡类型。每个实例都是通过相应的子类创建的,并传入相应的值和名称。值用于标识枚举类型的唯一性,而名称则是该类型的字符串表示。

CreditCard.Gold 来引用 Gold 信用卡的实例,并获取它的折扣率。在需要使用信用卡类型的地方,也可以直接使用 CreditCard 类型来表示。

FromName 和 FromValue

public class SmartEnumsTests
{
    private readonly ITestOutputHelper _testOutputHelper;

    public SmartEnumsTests(ITestOutputHelper testOutputHelper
    {
        _testOutputHelper = testOutputHelper;
    }

    /// <summary>
    /// 基本用法
    /// </summary>
    [Fact]
    public void Use(
    {
        var standardCard = CreditCard.FromName("Standard";
        var standardCard2 = CreditCard.FromValue(1;

        Assert.Equal(standardCard, standardCard2;
        _testOutputHelper.WriteLine(standardCard!.ToJson(;
    }
}

看完上述的示例代码,智能枚举最明显的好处应该非常直观:就是代码行数增加了亿点点,而不是一点点!

小结

智能枚举 = 枚举 + 丰富的行为

C# 枚举类型实现信用卡类型的示例。为了更好地实现该功能,我们创建了一个通用枚举类 Enumeration,并在此基础上实现了 CreditCard 类和其四个具体子类,分别表示不同类型的信用卡。

Discount 属性,表示该类型信用卡的折扣率。而 CreditCard 类中的静态实例则表示四种不同的信用卡类型。通过这种方式,我们可以轻松地定义和使用不同类型的信用卡,并在需要使用信用卡类型的地方直接使用 CreditCard 类型来表示。

编程笔记 » C# 中的“智能枚举”:如何在枚举中增加行为

赞同 (41) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽