24

技术债! 怎样简洁高效的实现多个 Enum 自由转换

 3 years ago
source link: http://www.cnblogs.com/huangxincheng/p/13850587.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一:背景

1. 讲故事

前段时间和同事负责一个项目的两个业务模块,可能大家缺少沟通,导致本该定义一个 Enum 的地方结果我俩各自定义了一个,导致后面这两个 Enum 进行对接就烦了,为了方便理解,也不想让大家看这崴脚的英文拼写,我就拿 银行 举例吧。

  • A同事 定义的枚举
public enum BankEnum
    {
        ICBC = 1,
        CMSB = 2,
        CMBC = 3
    }
  • B同事 定义的枚举
public enum ChinaBankEnum
    {
        中国民生银行 = 1,
        中国工商银行 = 2,
        中国招商银行 = 3,
    }

这就很尬尴了,怎么将 ChinaBankEnum 转成 BankEnum 呢? 为了寻求多快好省,本篇就聊聊这个问题。

二:寻找解决办法

1. 手工匹配

本质上就是找两个 Enum 的 mapping 关系,人肉匹配那是最简单粗暴的,代码如下:

static BankEnum ConvertToEnum(ChinaBankEnum chinaBank)
        {
            switch (chinaBank)
            {
                case ChinaBankEnum.中国工商银行: return BankEnum.ICBC;
                case ChinaBankEnum.中国民生银行: return BankEnum.CMSB;
                case ChinaBankEnum.中国招商银行: return BankEnum.CMBC;
            }

            return default(BankEnum);
        }

看的出来,这种写法缺少灵活性,作为程序员肯定不能满足于此,既然是找 mapping 关系,我相信很多朋友最早听说 mapping 一词是来源于 EntityFramework ,人家在处理 table 到 model 的 mapping 采用的是 Attribute,是不是这样,灵感就在于此,我是不是也可以使用 Attribute 来标记两个 Enum 的对应关系呢???

2. 使用 Attribute

有了这个思路,就可以自定义一个 Attribute,当然比较懒的话,也可以用 Framework 自带的 DescriptionAttribute ,代码如下:

[AttributeUsage(AttributeTargets.All)]
    public class DescriptionAttribute : Attribute
    {
        public DescriptionAttribute(){}

        public DescriptionAttribute(string description){}
    }

接下来就可以把 Description 套在 BankEnum 上,如下代码所示:

public enum BankEnum
    {
        [Description(nameof(ChinaBankEnum.中国工商银行))]
        ICBC = 1,

        [Description(nameof(ChinaBankEnum.中国民生银行))]
        CMSB = 2,

        [Description(nameof(ChinaBankEnum.中国招商银行))]
        CMBC = 3
    }

然后我可以通过反射拿到 Attribute 的值再去 ChinaBankEnum 中去找对应的 key 即可,对不对,为了方便理解,我封装一个 Enum 的扩展方法,通过反射实现 Enum 对 Enum 的转换,代码如下:

/// <summary>
    /// 枚举的扩展方法
    /// </summary>
    public static class EnumExtension
    {
        public static Target ConvertTo<Target>(this Enum enumValue) where Target : Enum
        {
            var key = Enum.GetName(enumValue.GetType(), enumValue);

            var fields = typeof(Target).GetFields();

            foreach (var field in fields)
            {
                var attribute = field.GetCustomAttribute<DescriptionAttribute>();

                if (attribute == null) continue;

                if (key == attribute.Description)
                {
                    var obj = (Target)field.GetValue(typeof(Target));
                    return obj;
                }
            }

            return default(Target);
        }
    }

代码逻辑还是比较简单的,接下来写两个例子测试下:

static void Main(string[] args)
        {
            ChinaBankEnum chinaBankEnum = ChinaBankEnum.中国工商银行;
            ChinaBankEnum chinaBankEnum2 = ChinaBankEnum.中国招商银行;

            var bankEnum = chinaBankEnum.ConvertTo<BankEnum>();
            var bankEnum2 = chinaBankEnum2.ConvertTo<BankEnum>();

            Console.WriteLine($"{chinaBankEnum} -> {bankEnum}\r\n{chinaBankEnum2} -> {bankEnum2}");
        }

aQ7FVfQ.png!mobile

3. 对 Parse 转换的一些优化

不知道大家在写代码的时候有没有发现将 string 或者 int 转成 Enum 的时候,写出来的代码是又臭又长,比如下面这样:

var bankEnum = (ChinaBankEnum)Enum.Parse(typeof(ChinaBankEnum), "中国工商银行");

又是 typeof 又是类型强转换,而且强转不过来的话还会抛异常,基于各种原因 framework 又新增了一个 TryParse,如下图所示:

Yb2Ib2z.png!mobile

看起来确实好多了,但还是觉得有点不爽,为了再顺眼一些,我决定在 EnumExtension 中再封装一个 TryParse 方法,如下代码所示:

public static class EnumExtension
    {
        public static T TryParse<T>(this string value) where T : struct
        {
            var isSucc = Enum.TryParse<T>(value, out var result);

            if (!isSucc) return default(T);

            return result;
        }
    }

调用的时候就可以这么来: var bankEnum = "中国工商银行".TryParse<ChinaBankEnum>(); ,是不是就顺眼多了哈。

三: 总结

哈,本篇就来自于项目开发中遇到的一个坑,相信很多朋友都会遇到类似的情况,遗憾的是默认的 Enum 提供的功能太弱,大家可以根据自己的业务在 Enum 上扩充更多实用的方法,如获取所有的key,所有的value 等等,让自己的代码更加整洁,干净,强大!

更多高质量干货:参见我的 GitHub: dotnetfly


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK