关于枚举类(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

相关内容

热门资讯

怎么解除订阅安卓系统,安卓系统... 你是不是也和我一样,手机里订阅了好多服务,结果现在想解除订阅,却一头雾水?别急,今天就来手把手教你如...
安卓系统停用怎么开启,轻松恢复... 亲爱的手机控们,你是否曾经遇到过安卓系统突然停用的情况,让你手忙脚乱,不知所措?别担心,今天就来教你...
安卓系统电池健康度,电池健康度... 你有没有发现,你的安卓手机最近是不是有点儿不给力了?电池续航能力大不如前,充电速度也慢了不少?别急,...
安卓系统按键怎么截图,安卓系统... 你是不是也和我一样,有时候想截个图分享给朋友,却发现安卓手机的截图功能有点神秘呢?别急,今天就来手把...
购票系统安卓源代码,架构设计与... 你有没有想过,那些我们每天离不开的购票系统,它们背后的秘密是什么呢?今天,就让我带你一探究竟,揭开购...
安卓手机系统后台测试,深度解析... 你有没有发现,你的安卓手机后台总是悄悄地忙碌着?别小看了这些后台程序,它们可是手机系统稳定运行的关键...
安卓系统重启的图标,解锁设备新... 手机突然重启,是不是心里有点慌?别急,今天就来和你聊聊安卓系统重启的图标,让你一眼就能认出它,再也不...
车载智慧屏安卓系统,智能出行新... 你有没有发现,现在的车载智慧屏越来越智能了?尤其是那些搭载了安卓系统的,简直就像是个移动的小电脑,不...
安卓系统连上网权限,解锁设备无... 你有没有发现,你的安卓手机里有些应用总是偷偷连上网?别小看这个小小的网络权限,它可是能影响你隐私、消...
安卓谷歌操作系统,探索安卓谷歌... 你知道吗?在智能手机的世界里,有一个操作系统可是无人不知、无人不晓,那就是安卓谷歌操作系统。它就像一...
安卓系统手写%怎样调出,具体实... 你有没有遇到过这种情况:在使用安卓手机的时候,突然想用手写输入法来记录一些灵感或者重要信息,可是怎么...
安卓手机重置 系统设置,轻松恢... 手机用久了是不是感觉卡顿得厉害?别急,今天就来教你怎么给安卓手机来个大变身——重置系统设置!想象你的...
win如何安装安卓系统,Win... 哇,你有没有想过,让你的Win系统也能玩转安卓应用?没错,就是那种在手机上轻松自如的安卓系统,现在也...
苹果qq和安卓系统,跨平台体验... 你有没有发现,现在手机市场上,苹果和安卓的较量可是越来越激烈了呢!咱们就来聊聊这个话题,看看苹果QQ...
显示最好的安卓系统,探索最新旗... 你有没有想过,为什么安卓系统那么受欢迎呢?它就像一个魔法盒子,里面装满了各种神奇的魔法。今天,就让我...
安卓app怎么降级系统,系统版... 你有没有发现,有时候安卓手机的系统更新后,新功能虽然炫酷,但老系统用起来更顺手呢?别急,今天就来教你...
雷军脱离安卓系统,引领科技变革... 你知道吗?最近科技圈可是炸开了锅,因为我们的雷军大大竟然宣布要脱离安卓系统,这可真是让人大跌眼镜啊!...
安卓系统自动开网络,安卓系统自... 你有没有发现,手机里的安卓系统有时候会自动开启网络连接,这可真是让人又爱又恨啊!有时候,你正专心致志...
安卓系统怎样控制后台,因为服务... 手机里的安卓系统是不是感觉越来越卡了?后台程序太多,不仅耗电还影响性能。别急,今天就来教你怎么巧妙地...
安卓系统打游戏推荐,一触即达! 你有没有发现,现在手机游戏越来越好玩了?不管是休闲小游戏还是大型MMORPG,都能在手机上畅玩。但是...