c# 怎样简洁高效的实现多个 Enum 自由转换

一:背景

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}");
    }

3. 对 Parse 转换的一些优化

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

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

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

看起来确实好多了,但还是觉得有点不爽,为了再顺眼一些,我决定在 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 等等,让自己的代码更加整洁,干净,强大!

以上就是c# 怎样简洁高效的实现多个 Enum 自由转换的详细内容,更多关于c# Enum 自由转换的资料请关注我们其它相关文章!

(0)

相关推荐

  • 关于C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用介绍方法

    C# 5.0 给我们带来了三个非常有用的编译器特性 CallerMemberName CallerFilePath CallerLineNumber 在C与C++中由下列字符帮助我们实现调试消息的文件行号 复制代码 代码如下: .#define debug_msg printf("%s[%d]:",__FILE__,__LINE__);printf 在.NET 4中与其功能相等的是 复制代码 代码如下: new StackTrace(true).GetFrame(1).GetMetho

  • C#使用LINQ中Enumerable类方法的延迟与立即执行的控制

    延时执行的Enumerable类方法 LINQ标准查询运算法是依靠一组扩展方法来实现的.而这些扩展方法分别在System.Linq.Enumerable和System.Linq.Queryable这连个静态类中定义. Enumerable的扩展方法采用线性流程,每个运算法会被线性执行.这种执行方法如果操作类似关系型数据库数据源,效率会非常低下,所以Queryable重新定义这些扩展方法,把LINQ表达式拆解为表达式树,提供程序就可以根据表达式树生成关系型数据库的查询语句,即SQL命令,然后进行相

  • C#中IEnumerable接口用法实例分析

    本文实例讲述了C#中IEnumerable接口用法.分享给大家供大家参考.具体分析如下: 枚举数可用于读取集合中的数据,但不能用于修改基础集合. 最初,枚举数定位在集合中第一个元素前.Reset 方法还会将枚举数返回到此位置.在此位置上,Current 属性未定义.因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数提前到集合的第一个元素. 在调用 MoveNext 或 Reset 之前,Current 返回同一对象.MoveNext 将 Current 设置为下一个

  • C# IEnumerable和IEnumerator接口浅析

    温故而知新,可以为师矣,有空经常复习一下基础知识是有必要的,并且能加深理解和记忆. Foreach常用于循环访问集合,对实现IEnumerable的接口的容器进行遍历,IEnumerable和IEnumerator接口我有时候也有点迷糊,按官方的解释,IEnumerable是枚举器接口,IEnumerator是迭代器接口,从字面意思来看相差不大,逐一分析一下. IEnumerable接口 public interface IEnumerable { IEnumerator GetEnumerat

  • C#中IEnumerable、ICollection、IList、List之间的区别

    首先我看看 IEnumerable: // 摘要: // 公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代. // // 类型参数: // T: // 要枚举的对象的类型. [TypeDependency("System.SZArrayHelper")] public interface IEnumerable<out T> : IEnumerable { // 摘要: // 返回一个循环访问集合的枚举器. // // 返回结果: // 可用于循环访问集合的 Syst

  • 基于C#中IDisposable与IEnumerable、IEnumerator的应用

    C#中如何合理的释放非托管内存?在本文中我们将讲解使用IDisposable释放托管内存和非托管内存. A.首先需要让类实现IDisposable接口,然后实现IDispose方法. A.a核心Disponse(bool isDisponse) 1.此方法首先判断isReadyDisposed(判断是否第一次调用此核心方法),如果不是第一次调用则不做任何操作. 2.再判断是否是析构函数调用?如果是析构函数调用不释放托管资源,其交由GC进行释放,如果析构函数释放托管资源可能之前GC释放过,就会导致

  • c# EnumHelper枚举常用操作类

    测试代码如下: namespace CutPictureTest.Comm { public class EnumHelper { public static System.Collections.ArrayList GetName(Type enumType) { System.Collections.ArrayList arr = new System.Collections.ArrayList(); string[] n = System.Enum.GetNames(enumType);

  • C#中的IEnumerable简介及简单实现实例

    IEnumerable这个接口在MSDN上是这么说的,它是一个公开枚举数,该枚举数支持在非泛型集合上进行简单的迭代.换句话说,对于所有数组的遍历,都来自IEnumerable,那么我们就可以利用这个特性,来定义一个能够遍历字符串的通用方法. 下面先贴出code. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; us

  • C#中的IEnumerable接口深入研究

    C#和VB.NET中的LINQ提供了一种与SQL查询类似的"对象查询"语言,对于熟悉SQL语言的人来说除了可以提供类似关联.分组查询的功能外,还能获取编译时检查和Intellisense的支持,使用Entity Framework更是能够自动为对象实体的查询生成SQL语句,所以很受大中型信息系统设计者的青睐. IEnumerable这个接口可以说是为了这个特性"量身定制",再加上微软提供的扩展(Extension)方法和Lambda表达式,给开发者带来了无穷的便利.

  • C#中enum和string的相互转换

    C# Json转换操作 枚举类型 Enum为枚举提供基类,其基础类型可以是除 Char 外的任何整型,如果没有显式声明基础类型,则使用Int32. 注意:枚举类型的基类型是除 Char 外的任何整型,所以枚举类型的值是整型值 1.C#将枚举转为字符串(enume->string) 我们的对象中包含枚举类型,在序列化成Json字符串的时候,显示的是枚举类型对应的数字.因为这是枚举的 本质所在,但是很多时候需要在JSON转化的时候做一些操作,使之显示字符串,因为用户需要字符串. 方法就是:在枚举类型

  • C#中FlagsAttribute属性在enum中的应用详解

    Net C#中枚举的声明格式如下所示: [attributes] [modifiers] enum identifier [:base-type] {enumerator-list} [;] FlagsAttribute属性就是枚举类型的一项可选属性.它的主要作用是可以将枚举作为位域处理(P.S. C#不支持位域). 所谓位域 是单个存储单元内相邻二进制位的集合. 通过为枚举添加这个属性,可以改变枚举的一些行为来满足我们的需要. enum MyFlags { Flag1, Flag2, Flag

  • C# IQueryable及IEnumerable区别解析

    在使用EF查询数据的时候,我们常用的查询数据方式有linq to sql,linq to object, 查询返回的结果有两种类型:IQueryable.IEnumerable,两者内部的处理机制是完全不同的. 清楚认识,这里也是一个数据查询的优化点. 在System.linq命名空间,有两个静态类:Queryable和Enumerable. 在System.linq.Queryable中,参数接收的是一个表达式类型,返回IQueryable接口 public static IQueryable

随机推荐