利用Mono.Cecil制作出枚举类的泛型扩展方法

我的项目的某部分需要经常判断枚举的某个标识的存在,就经常出现类似于“(A & B) == B;”这样的代码。不过我看着觉得不爽,也觉得很繁琐。我知道Enum类有HasFlag()方法,但是这个会导致装箱,智能感知也感知不出来,不好。于是我就写了下面的代码:

1  public   static   bool  Has < TEnum > ( this  TEnum rpEnum, TEnum rpValue)
2       where  TEnum :  struct , Enum
3  {
4       return  (rpEnum  &  rpValue)  ==  rpValue;
5  }

可是这代码无法通过编译

Image1

因为在编译器的眼中,Enum是个特殊类型,无法作为泛型约束(参考:http://msdn.microsoft.com/zh-cn/library/56b2hk61.aspx

难道这个只能是空想吗?非也

经过搜索,发现在C++/CLI里没有这个限制(参考:http://social.msdn.microsoft.com/Forums/en-US/vs2010ctpvbcs/thread/5844d719-3ef6-4000-913c-27c108269e71)。但我的基础类库不可能全部换用C++/CLI写吧(要知道C++/CLI的语法弄得我头都大了)

不过想想,在C++/CLI可以通过编译,那就说明在IL方面是可以支持枚举作为泛型约束,只是C#编译器有问题而已。于是我就想到了Mono.Cecil这个神器,用它修改编译后的程序集来实现我要的方法(我的基础类库也不可能全部换用IL来写吧)

修改

先弄个伪方法:

ContractedBlock.gif ExpandedBlockStart.gif View Code
1  public   static   bool  Has < TEnum > ( this  TEnum rpEnum, TEnum rpValue)
2       where  TEnum :  struct
3  {
4       return   true ;
5  }

然后新建一个程序,把Mono.Cecil导入进去(下载:http://www.mono-project.com/Cecil

下面是主要代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 1  static   void  Main( string [] args)
 2  {
 3       // 读取程序集
 4      var rAssembly  =  AssemblyDefinition.ReadAssembly( " Moen.Base.dll " );
 5        // 获取指定的类
 6      var rType  =  rAssembly.MainModule.GetType( " Moen " " EnumExtensions " );
 7        // 获取指定的方法
 8       var rMethod  =  rType.Methods.Single(r  =>  r.Name  ==   " Has " );
 9 
10       // 修改泛型约束
11      var rGenericParameter  =  rMethod.GenericParameters[ 0 ];
12      rGenericParameter.Constraints[ 0 =  rAssembly.MainModule.Import( typeof (Enum));
13       // 修改最大栈大小(这个功能需要压栈两次)
14      rMethod.Body.MaxStackSize  =   2 ;
15       // 清空方法体代码
16      rMethod.Body.Instructions.Clear();
17       // 修改方法体代码
18      var rProcessor  =  rMethod.Body.GetILProcessor();
19      rProcessor.Append(rProcessor.Create(OpCodes.Ldarg_0));
20      rProcessor.Append(rProcessor.Create(OpCodes.Ldarg_1));
21      rProcessor.Append(rProcessor.Create(OpCodes.And));
22      rProcessor.Append(rProcessor.Create(OpCodes.Ldarg_1));
23      rProcessor.Append(rProcessor.Create(OpCodes.Ceq));
24      rProcessor.Append(rProcessor.Create(OpCodes.Stloc_0));
25      rProcessor.Append(rProcessor.Create(OpCodes.Ldloc_0));
26      rProcessor.Append(rProcessor.Create(OpCodes.Ret));
27 
28       // 保存修改
29      rAssembly.Write( " Moen.Base.dll " );
30  }

PS:修改部分的那串代码,是我先编译一个指定枚举的同样功能的方法,然后从反编译出的IL代码中得到的

最后

运行修改程序,类库就被修改了。查看方法的签名,就会像这样。C#编译器可是做不到呀

为什么约束那里除了Enum还有一个struct呢,因为我不想让已装箱的Enum对象在此方法中搞破坏(抛出异常),必须是实际的枚举类

PS:不要吐槽图中的Object2(如果你想知道真相的话,不过很简单)

Image2

下面是测试

Image3

这样问题解决了。(为了减少操作量,可以在类库的生成后事件中调用这个修改程序)

总结

Mono.Cecil可以很方便地使枚举类成为泛型约束条件,也能做到许多意想不到的东西。当然,我希望下一个版本的C#编译器能去除这个限制

Url: http://moen.cnblogs.com/archive/2011/07/14/use-mono-cecil-to-create-generic-extension-method-of-enum-class.html

转载于:https://www.cnblogs.com/Moen/archive/2011/07/14/use-mono-cecil-to-create-generic-extension-method-of-enum-class.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值