博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用Mono.Cecil制作出枚举类的泛型扩展方法
阅读量:4590 次
发布时间:2019-06-09

本文共 2294 字,大约阅读时间需要 7 分钟。

我的项目的某部分需要经常判断枚举的某个标识的存在,就经常出现类似于“(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是个特殊类型,无法作为泛型约束(参考:)

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

经过搜索,发现在C++/CLI里没有这个限制(参考:)。但我的基础类库不可能全部换用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导入进去(下载:)

下面是主要代码:

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:

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

你可能感兴趣的文章
c# 引用参数-ref
查看>>
c# 多态
查看>>
c# 参数数组
查看>>
c# 虚属性
查看>>
c# 子类的声明
查看>>
c# 类嵌套
查看>>
c# 方法的隐藏
查看>>
c# 接口实现
查看>>
c# 密封类
查看>>
c# is运算符
查看>>
c# 抽象类与接口
查看>>
c# 委托
查看>>
c# 接口使用
查看>>
c# 事件
查看>>
c# as运算符
查看>>
c# 调试过程
查看>>
c# 结构
查看>>
C# 中的异常处理
查看>>
c# 调试
查看>>
c# 使用序列化
查看>>