关于枚举类(Enumeration Class )
admin
2024-03-20 15:43:45
0

        在微软的微服务架构案例eshopOnContainer中,对于订单的状态是这样的:

public class OrderStatus: Enumeration
{public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());public OrderStatus(int id, string name): base(id, name){}
}

        微软没有直接使用传统的Enum关键字,而是自定了以一个抽象类:Enumeration。最开始,我有些不解,为什么么要“舍近求远”?其实微软官方在此文中提到了枚举类,理由是启动面向对象语言的所有丰富功能。解释的很晦涩,虽然给出了代码案例,但是还是让人很难体会到其中奥妙。直到最近又看了这个博客,似乎才深入理解了一些。结合一个例子说明,可能更好!

目录

1.使用枚举

2.enumeration类

3.拓展



1.使用枚举

        假设你要去买咖啡,正常情况下会有“大、中、小”三种规格,所以一般情况下会让你给出尺寸需求,不同的大小价格自然不同,出于节约,默认是小杯。好了我们应该怎么实现呢?

首先定义一个枚举类型,表明所要分量:

    public enum CoffeCupType{Middle,Large,SuperLarge}

        然后我们定义咖啡类如下:

    public class Coffee{const double CoffeBeans = 10.0;public CoffeCupType Type { get; set; } = default;public double Pay(){return Type switch{CoffeCupType.Middle => CoffeBeans * 1.0,CoffeCupType.Large => CoffeBeans * 1.5,CoffeCupType.SuperLarge => CoffeBeans * 2.0,_ => 0,};}public void SetType(int t){Type = (CoffeCupType)t;}}

可以看到我们设计了一个很简单的咖啡类,咖啡的费用需要根据类型判断,看起来好像没什么问题,但是还是有些不完美的地方:

  • 与枚举相关的行为会分部到应用的多个地方
  • 如果要增加新的枚举值,可能要对代码进行
  • ,枚举类型不符合开闭原则

添加新的枚举值,就需要在Pay函数中添加新的Switch分支,让默认分支变得有防御性,但是新的枚举值可能导致错误的逻辑。

        其实,对于上面的例子,我们将杯子的属性和价格倍率拆分了,使得在Coffe类中显得耦合性过强。其实价格倍率本身应该是CoffeeCupType的附加属性,所以,如果我们能定义一个类集合,那么上面的逻辑就会简化很多。

2.enumeration类

        这里作者定义了一个抽象类:

 public abstract class Enumeration{private readonly int _value;private readonly string _name;protected Enumeration(){ }protected Enumeration(int value, string name){_value = value;_name = name;}public int Value { get { return _value; } }public string Name { get { return _name; } }public override string ToString(){return Name;}public static IEnumerable GetAll() where T:Enumeration{var type=typeof(T);var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);return fields.Select(f=> f.GetValue(null)).Cast();}public override bool Equals(object? obj){if (obj is not Enumeration otherObj)return false;var typeMatcher=GetType().Equals(obj.GetType());var valueMatcher=_value.Equals(otherObj.Value);return typeMatcher && valueMatcher;}public override int GetHashCode(){return Value.GetHashCode();}public static int AbsoluteDifference(Enumeration firstValue,Enumeration secondValue){var absoluteDifference=Math.Abs(firstValue.Value - secondValue.Value);return absoluteDifference;}public static T FromValue(int value) where T:Enumeration,new(){var matchingItem = Parse(value, "value", item => item.Value == value);return matchingItem;}public static T FromValue(string name) where T:Enumeration,new(){var matchingItem=Parse(name, "name", item => item.Name == name);return matchingItem;}public static T Parse(K value,string description,Func predicate) where T:Enumeration{var matchingItem=GetAll().FirstOrDefault(predicate);if(matchingItem == null){throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");}return matchingItem;}public int CompareTo(object other)=>Value.CompareTo(((Enumeration)other).Value);}

 具体代码我就不讲解了,成员属性都很好理解。有了上面的枚举类,我们可以将CoffeeCupType类派生于此,然后定义三个静态成员,以此限定范围。

 public class CoffeeCupType2:Enumeration{public static readonly CoffeeCupType2 Middle = new CoffeeCupType2(0, "中杯",1.0);public static readonly CoffeeCupType2 Large = new CoffeeCupType2(1, "大杯",1.5);public static readonly CoffeeCupType2 SuperLarge = new CoffeeCupType2(2, "特大杯",2.0);public CoffeeCupType2() { }public CoffeeCupType2(int value,string displayName,double rate):base(value, displayName){_rate = rate; }private readonly double _rate;public static IEnumerable List()=>new[] {Middle,Large,SuperLarge};public static CoffeeCupType2 FromName(string name){var state= List().Single(s => string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase));if(state==null){throw new InvalidDataException("Invalid Name!");}return state;}public static CoffeeCupType2 FromValue(int value){var state = List().Single(s => s.Value==value);if (state == null){throw new InvalidDataException("Invalid Value!");}return state;}public  double GetRate()=>_rate;public  void EntryWord() => Console.WriteLine($"你现在换成了{Name}");}

        这下,就可以将枚举当成一个完整的类来使用,这个类很像DDD中定义的“值对象”,然后获取支付订单的函数就变为:

        public double GetPay(){return CoffeCupType_2!.GetRate() * CoffeBeans;}

        优化支付函数,避免Switch分支语句只是其中的一个好处,你还可以在CoffeCupType2中添加一些行为函数,让其作为应用中作为“状态”时,变成“充血模型”。

        你还可以创建子类型:

        private class GlassBottle:CoffeeCupType2{public GlassBottle() : base(3, "畅饮杯", 3.0) { }public new void EntryWord() => Console.WriteLine($"只有尊贵的VIP才可以买{Name}装咖啡,可以无限续杯");}public static readonly CoffeeCupType2 Free = new GlassBottle();

这个类型不会暴露给外面。如果你看过里面的代码,就注意到了我还添加了一个函数:EntryWord,也就是带有一定内建行为,我想在替换杯子类型时,能够出发这个行为:

因此在Coffe中,给类型赋值时会调用这个函数:

        private CoffeeCupType2? _coffeCupType2;public CoffeeCupType2? CoffeCupType_2 {get { return _coffeCupType2; }set{if(value is CoffeeCupType2 c){_coffeCupType2 = c;_coffeCupType2.EntryWord();}else{_coffeCupType2 = CoffeeCupType2.Middle;}} } 

        枚举类在你需要构建充血模型时很有用,你可以让这些枚举值附带特定的行为逻辑,这对于构建DDD中的领域模型时很有帮助的,但是并不是什么时候都需要枚举类。在某些简单的场景,还是用普通枚举即可。

3.拓展

        值得一提的是,枚举类已经有人实现了这个类库,而且相当强大,这样就不要自己写Enumeration抽象类了。具体可以在github上搜SmartEnum。

Nuget地址:SmartEnum。

本文示例代码:EnumerationClass

相关内容

热门资讯

安卓系统的如何测试软件,从入门... 你有没有想过,你的安卓手机里那些神奇的软件是怎么诞生的呢?它们可不是凭空出现的,而是经过一系列严格的...
小米8安卓系统版本,安卓系统版... 你有没有发现,手机更新换代的速度简直就像坐上了火箭呢?这不,小米8这款手机自从上市以来,就凭借着出色...
华为手机安卓系统7以上,创新体... 你有没有发现,最近华为手机越来越受欢迎了呢?尤其是那些搭载了安卓系统7.0及以上版本的机型,简直让人...
儿童英语免费安卓系统,儿童英语... 哇,亲爱的家长朋友们,你是否在为孩子的英语学习发愁呢?别担心,今天我要给你带来一个超级好消息——儿童...
ios系统切换安卓系统还原,还... 你有没有想过,有一天你的手机从iOS系统切换到了安卓系统,然后再从安卓系统回到iOS系统呢?这听起来...
灵焕3装安卓系统,引领智能新体... 你知道吗?最近手机圈里可是掀起了一股热潮,那就是灵焕3这款神器的安卓系统升级。没错,就是那个曾经以独...
安卓系统指南针软件,探索未知世... 手机里的指南针功能是不是让你在户外探险时倍感神奇?但你知道吗,安卓系统中的指南针软件可是大有学问呢!...
华为是不用安卓系统了吗,迈向自... 最近有个大新闻在科技圈里炸开了锅,那就是华为是不是不再使用安卓系统了?这可不是一个简单的问题,它涉及...
安卓系统热点开启失败,排查与解... 最近是不是你也遇到了安卓系统热点开启失败的小麻烦?别急,让我来给你详细说说这个让人头疼的问题,说不定...
小米max2系统安卓,安卓系统... 你有没有听说过小米Max2这款手机?它那超大的屏幕,简直就像是个移动的电脑屏幕,看视频、玩游戏,那叫...
电池健康怎么保持安卓系统,优化... 手机可是我们生活中不可或缺的好伙伴,而电池健康度就是它的生命力。你有没有发现,随着使用时间的增长,你...
安卓手机怎么调系统颜色,安卓手... 你有没有发现,你的安卓手机屏幕颜色突然变得不那么顺眼了?是不是也想给它换换“脸色”,让它看起来更有个...
安卓系统清粉哪个好,哪款清粉工... 手机用久了,是不是觉得卡得要命?别急,今天就来聊聊安卓系统清理垃圾哪个软件好。市面上清理工具那么多,...
华为被限制用安卓系统,挑战安卓... 你知道吗?最近科技圈可是炸开了锅!华为,这个我们耳熟能详的名字,竟然因为一些“小插曲”被限制了使用安...
安卓系统是不是外国,源自外国的... 你有没有想过,我们每天离不开的安卓系统,它是不是外国货呢?这个问题听起来可能有点奇怪,但确实很多人都...
安卓系统缺少文件下载,全面解析... 你有没有发现,用安卓手机的时候,有时候下载个文件真是让人头疼呢?别急,今天就来聊聊这个让人烦恼的小问...
kktv系统刷安卓系统怎么样,... 你有没有听说最近KKTV系统刷安卓系统的事情?这可是个热门话题呢!咱们一起来聊聊,看看这个新玩意儿到...
安卓系统连接电脑蓝牙,操作指南... 你有没有遇到过这种情况:手机里堆满了各种好用的应用,可就是想找个方便快捷的方式,把手机里的音乐、照片...
安卓车机11.0系统包,智能驾... 你有没有发现,最近你的安卓车机系统好像悄悄升级了呢?没错,就是那个安卓车机11.0系统包!这可不是一...
安卓系统最高到多少,从初代到最... 你有没有想过,你的安卓手机系统升级到哪一步了呢?是不是好奇安卓系统最高能到多少呢?别急,今天就来带你...