目录

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

枚举的基本用法回顾

以下是一个常见的 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)(((int)today + 1) % 7) + "。");
Console.WriteLine("昨天是" + (Weekday)(((int)today + 6) % 7) + "。");
}
}

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

接下来,我们计算并输出明天和昨天的日子。我们使用强制类型转换将枚举值转换为整数,然后在取模 7 意义下加或减 1 或 6,以便正确地计算出前一天或后一天的日子。

输出结果应该是这样的:

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

枚举常见的设计模式运用

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

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

介绍

  1. 状态模式

状态模式用于根据对象的内部状态来改变其行为。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>> 接口,以支持比较操作。

这个抽象类包含了一些常用的枚举操作方法,例如 FromNameFromValue,它们可以通过名称或值来获取枚举值。它还重写了 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 类型来表示。

我们平时也可以这样使用,通过 FromNameFromValue

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# 中的“智能枚举”:如何在枚举中增加行为的更多相关文章

  1. 【转载】[C#]枚举操作(从枚举中获取Description,根据Description获取枚举,将枚举转换为ArrayList)工具类

    关键代码: using System; using System.Collections; using System.Collections.Generic; using System.Compone ...

  2. Java enum枚举在实际项目中的常用方法

    在项目实际开发过程中,经常会遇到对某些固定的值.字典项的定义的需求,很多项目经常使用常量来定义,其实在jdk1.5就已经引入了枚举,使用枚举可以更好的解决这类需求,本文主要记录枚举的优势以及经常在项目 ...

  3. PyTables 教程(三) 执行撤消/重做功能,使用枚举类型,表中的嵌套结构

    翻译自http://www.pytables.org/usersguide/tutorials.html 执行撤消/重做功能 PyTables 支持撤销/重做功能,此功能可让您将标记放置在层次结构操作 ...

  4. EnumHelper.cs枚举助手(枚举描述信息多语言支持)C#

    C#里面经常会用到枚举类型,枚举是值类型对象,如果你想用枚举类型的多属性特性,或者你想在MVC页面上通过简单的值类型转换,将某字段值所代表的含义转换为文字显示,这时候必须要将枚举扩展,是它支持文本描述 ...

  5. 使用EF自带的EntityState枚举和自定义枚举实现单个和多个实体的增删改查

    本文目录 使用EntityState枚举实现单个实体的增/删/改 增加:DbSet.Add = > EntityState.Added 标记实体为未改变:EntityState.Unchange ...

  6. swift_枚举 | 可为空类型 | 枚举关联值 | 枚举递归 | 树的概念

    ***************可为空的类型 var demo2 :we_demo = nil 上面这个代码串的语法是错的 为什么呢, 在Swift中,所有的类型定义出来的属性的默认值都不可以是nil ...

  7. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  8. c语言的枚举(遍历枚举)与数据类型总结

    一.枚举的概念 枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数.当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型. 比如,你可以用一个枚举类型的变量来表示季节, ...

  9. Kendo Grid控件中将枚举值转为枚举名显示

    我们在开发过程中经常会遇到需要将枚举值转换成名称进行显示的情况.如下我们有这样一个数据源对象: var users = [ {id: 1, name: "similar1", st ...

  10. Day 12:枚举值、枚举类

    jdk1.5新特性之-----枚举 问题:某些方法所接收的数据必须是在固定范围之内的,  解决方案: 这时候我们的解决方案就是自定义一个类,然后是私有化构造函数,在自定义类中创建本类的对象对外使用. ...

随机推荐

  1. Android进度表示

    在连接上数据库之后,一切都变得简单了呢! 开心,很轻松地就能够将APP里面的相关内容写完啦! 尝试了好久的连接Mysql数据库,最后还是没有成功: 虽然Android studio里面自带的SQLit ...

  2. EF Code 如何应对高并发

    1.高并发的情况,时常会发生数据不稳定的情况 在看本节内容之前,请先看上一章SqlServer 高并发的情况下,如何利用锁保证数据的稳定性 本节内容,也是具体讨论如何在EF中实现这些操作 2.场景模拟 ...

  3. 暗夜发光,独自闪耀,盘点网页暗黑模式(DarkMode)下的特效和动效,CSS3实现

    众所周知,网页的暗黑模式可以减少屏幕反射和蓝光辐射,减少眼睛的疲劳感,特别是在夜间使用时更为明显.其实暗黑模式也给霓虹灯效应(Neon Effect)提供了发挥的环境. 霓虹灯效应是一种视觉效果,其特 ...

  4. PHP微信三方平台-代公众号发起微信支付(jsAPI)

    一.前期准备工作 1.微信公众号需要开通微信支付认证将获取的秘钥给三方平台 2.添加支付回调域名地址:填写三方平台域名地址即可(最多5个) 二.代码demo 1.完成支付类 <?php /** ...

  5. 使用webpack 优化自己的项目。

    一.首先要了解概念:module,chunk 和 bundle 到底是什么? module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字: 我们直接写出来的是 m ...

  6. selenium验证码处理-获取验证码图片二进流数据转成原图保存

    1.因为视频的作者给的代码不完整,只有核心部分的代码. 2.视频作者示例使用的第三方破解12306的脚本网页(失效了) 所以本人无法复现,此次截取部分代码作为理解核心意思(思想方法最重要) 1.面向对 ...

  7. Flask快速入门day02(1、CBV使用及源码分析,2、模板用法,3、请求与响应的基本用法,4、session的使用及源码分析,5、闪现,6、请求扩展)

    目录 Flask框架 一.CBV分析 1.CBV编写视图类方法 二.CBV源码分析 1.CBV源码问题 2.补充问题 3.总结 三.模板 1.py文件 2.html页面 四.请求与响应 1.reque ...

  8. postgresSQL Extended Query执行过程和sharding-proxy的处理

    pg Extended Query PostgreSQL: Documentation: 15: 55.2. Message Flow 多个阶段,可复用 Parse → DESCRIBE statem ...

  9. Nuxtjs实现服务端渲染和静态化站点

    本文将介绍如何使用Nuxtjs对vue项目进行ssr和静态化处理. Nuxtjs简单介绍 首先,我们简单了解下Nuxtjs框架,Nuxt.js是一个基于Vue的通用框架,主要用于解决Vue项目的服务端 ...

  10. Spring @Profile注解使用和源码解析

    介绍 在之前的文章中,写了一篇使用Spring @Profile实现开发环境,测试环境,生产环境的切换,之前的文章是使用SpringBoot项目搭建,实现了不同环境数据源的切换,在我们实际开发中,会分 ...