与泛型类反射的遭遇 (网上转摘)
与泛型类反射的遭遇
自从采用了类似于MS的PetShop4.0的经典架构后,无论是技术层面也好,设计思想也罢,都围绕着这个架构体系在我自己的应用领域中逐步实践和体会。从贯彻PetShop的WebForm版本到自己拼装的混合WinForm 版本,再到全部基于WebService的解决方案。我太沉迷于这个体系架构了,甚至有很多不能理解的地方都是一股脑地全盘模仿,这样的依赖可不是什么好事。
在实际应用中,PetShop自然是不可能满足于千变万化的需求。所以做事有自己的思想是很重要的,才华就快横溢的我自然也非常注重。个人愚见,PetShop的重要技术主要是分层、反射和缓存,对于每个技术的详细理解等我以后认为自己完全参透抑或哪天自信心膨胀到足够自信了再另起篇幅论之。就目前而言,觉得比较可惜的是PetShop没有提供关于泛型的使用支持,所以泛型部分的想法都是自己光着膀子摸过来的,因为看好可谓是设计模式特例的泛型,就算全裸也是要尝试的。说了半天,才到正题。因为对象操作类的操作方法层面(也叫业务外观层面)和数据实现层面的依靠接口层面来进行关联,并以反射技术对数据实现层面的类进行动态加载。在PetShop中因为没有泛型,所以很好理解,用Assemly类的静态方法Load加载指定的程序集后,再用CreateInstance方法创建指定名称的类实例。代码通常如下:
strPath = “KfxyWebSite_SQLServerDAL”;
className = strPath + ".Person.Employee";
(IEmployee)Assembly.Load(strPath).CreateInstance(className);
其中strPath为程序集名称,也就是编译的项目名称或namespace根名称;className为strPath+子命名空间+类名称。
但当反射遭遇了泛型类,整个过程就貌似没有这么简单了。一开始无知的我仍然按普通类的方法去获取泛型类的实例,结果自然不能成功创建。花了一天时间的资料查询和反复实验,豁然觉得自己原来对反射所知竟是如此肤浅。可参考的方法查不到几个,众多问题似乎都避开了这个话题,偶尔搜索到论坛中和我一样也有问如何创建泛型类实例的问题,一看都是无人回答、无人问津。唯一线索只是知道泛型类的反射和普通类的反射是完全不同的概念,其主要区别应该在于CreateInstance时的className字符串参数的格式。在彻夜未眠的情况下,我仍然不得其解,只是从经验和信念上觉得可行性是99%以上,只是尚未找到方法。幸好在技术群朋友的提示下,给了我一个较为复杂的反射创建泛型类实例的示例。大致思路是加载程序集后,遍历程序集中所有的类,利用.Net2.0对Type类新增的对泛型类支持的一些方法来识别出泛型类,并将泛型类的模版类以参数形式添加到该类型中,从而生成一个泛型类。代码如下:
//泛型类接口实例。
IDictionary<TeacherTypeInfo, int, string> dal = null;
//根据指定的程序集名称加载程序集。
Assembly asm = Assembly.Load(strPath);
//获取程序集中所有的类。
Type[] types = asm.GetTypes();
//遍历类集合。
foreach (Type typeX in types)
{
//若该类型为泛型类。
if (typeX.IsGenericType)
{
//创建泛型类,其模版类以参数形式添加到该类型。
Type t = typeX.MakeGenericType(typeof(TeacherTypeInfo), typeof(int), typeof(string));
//使用Activator类创建该类型的实例。
dal = (IDictionary<TeacherTypeInfo, int, string>)Activator.CreateInstance(t);
}
}
以上是我整理精简后的代码,到此思路豁然开朗,两天以来的痛苦挣扎也终于见到了解脱的曙光,心里感谢那个技术朋友无数遍。但同时还是存有疑问,觉得似乎不应该像那位朋友所述对于泛型类只有这样的方法,毕竟感觉还是稍微复杂了些,从设计角度来说,没有理由是不能让第一种方法不成立的。于是调试了MakeGenericType后的类,看到了如下的格式:
KfxyWebSite_SQLServerDAL.Profile.Dictionary`3
[
[BlackTeam.ModelEducation.Person.TeacherTypeInfo, BlackTeam.ModelEducation, Version=1.0.0.4, Culture=neutral, PublicKeyToken=null]
,[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]
,[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]
]
Site_SQLServerDAL.Profile.Dictionary`3代表要创建的泛型类全名称,其中后面的上顿号3代表该泛型的模版类个数,该泛型类有三个模版类。后面便在[ ]内逐个放置各个模版类型。分析模版类型的格式,主要有类的FullName和Assembly.FullName两部分组成。
分析到此,不禁立即动手实验,采用原先的反射方法,一举成功,不禁长吁口气,看来自己的先前的判断未曾有错,只是差了一口气,憋了我两天。现在更为精简的代码如下:
className = strPath + ".Profile.Dictionary`1";
Type t = typeof(TeacherTypeInfo);
className += "[[" + t.FullName + "," + t.Assembly.FullName + "]]";
IDictionary<TeacherTypeInfo>
dal =(IDictionary<TeacherTypeInfo>)Assembly.Load(strPath).CreateInstance(className);
这里的泛型类只用了一个模版类,之后就是按照泛型类的格式组织字符串。
泛型+反射的遭遇由此结束,虽然郁闷了整整两天,总算是找到了一个较为满意的方法,也受益颇多。将此问题及解决过程记于Blog,望能给各位技术同行共同分享。
浙公网安备 33010602011771号