java泛型,使用 MSIL 为 Enum.HasFlag 封装一个泛型的扩展方法

.NET 框架 4.0 为 Enum 类型新增了一个 HasFlag 方法,这样每一个枚举类型(并不只限于“System.Enum”这个混蛋类型)的值都可以使用 HasFlag 来检查是不是设置了某个标志。
但是由于这个 HasFlag 方法接受的参数是 Enum 类型,而不是泛型的,在使用 Visual Studio (或者 Express)编写代码的时候就会发现智能感知无法在 flag 参数处提示期望的枚举类型,虽然这只是一个小毛病,但我觉得这样很不好。
可是,如果尝试创造一个泛型的 HasFlag 方法的话,就会发现 Enum 类型被视为特殊类型,不可以作为泛型约束(参考:http://msdn.microsoft.com/zh-cn/library/56b2hk61.aspx)。
所以对于此类的关于枚举的扩展方法的问题,普遍的解决方案是指定 struct、IConvertable 等类型约束,然后在运行时判断类型是否为枚举类型(如:http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum)。
可我还是觉得很不舒服,大概是因为我有一点洁癖吧,我总觉得在运行时判断类型特性好像有点性能低下呢。
不过,创建一个仅限定枚举类型的泛型约束是可能的,上面的关于泛型约束的类型限制也只是 C# 编译器的限制,实际上托管 C++ 和 MSIL 是没有这个限制的(#1093531)。
了解到这些之后,我就开始了使用托管 C++ 和 MSIL 来创建这样的扩展方法的尝试。
在经过两三个个小时的努力之后,最终用 MSIL (托管 C++ 什么的我实在是搞不好,哈哈哈……)成功编译出了可以正确运作的、使用 Enum 类型作为泛型约束的泛型扩展方法,作为对 Enum.HasFlag 的封装,我给它取了个简单的名字,“Has”。
为了编译这个扩展方法,也单独创建了一个程序集,虽然有点大炮打蚊子的感觉,不过还是相信 CLR 的 JIT 技术吧!
整个程序集的 MSIL 都是在“先编译一个程序集,然后使用 ILDASM 反汇编”的基础之上进行修改的,原理什么的也实在是没啥好讲的,完整的源代码如下:
.assembly GenericEnumHas { .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 29 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ..).NETFramework 2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 2C 50 72 // ,Version=v4.0,Pr 6F 66 69 6C 65 3D 43 6C 69 65 6E 74 01 00 54 0E // ofile=Client..T. 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 // .FrameworkDispla 79 4E 61 6D 65 1F 2E 4E 45 54 20 46 72 61 6D 65 // yName..NET Frame 77 6F 72 6B 20 34 20 43 6C 69 65 6E 74 20 50 72 // work 4 Client Pr 6F 66 69 6C 65 ) // ofile //.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) //.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx // 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) //.hash algorithm 0x00008004 //.ver 0:0:0:0 } .class public abstract auto ansi sealed beforefieldinit GenericEnumHelper.EnumHelper extends [mscorlib]System.Object { .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig static bool Has<([mscorlib]System.Enum) T>(!!T target, !!T 'flags') cil managed { .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ldarga.s target ldarg.1 box !!T constrained. !!T callvirt instance bool [mscorlib]System.Enum::HasFlag(class [mscorlib]System.Enum) ret } }
使用 ILASM 将上面的代码编译之后就可以在项目中使用了,我使用的编译参数是 /dll /optimize /fold。
测试的代码也很简单:
using System; using GenericEnumHelper; using System.Diagnostics; namespace EnumHelperTest { [Flags] enum MyEnum { Zero = 0, _disibledevent=>
至于输出的文本直观不直观好不好读懂之类的问题您就别跟我认真了罢!
如果你在 Visual Studio (Express) 里面查看这个 Has 扩展方法的签名的话,你还会看到像下面的这样的代码:
#region 程序集 GenericEnumHas.DLL, v4.0.30319 // ... \GenericEnumHas.DLL #endregion using System; using System.Runtime.CompilerServices; namespace GenericEnumHelper { public static class EnumHelper { public static bool Has(this T target, T flags) where T : Enum; } }
不过这段代码是绝对绝对完全完全不可能通过编译的~
Tags:  什么时候用泛型 泛型类 什么是泛型 泛型编程 java泛型

延伸阅读

最新评论

发表评论