C#动态调用泛型类、泛型方法
在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考。
主程序加载另一个程序集,将其中的所有类取出,然后对这些类分别调用泛型类或泛型方法。控制台程序解决方案如下:
-
main工程:提供worker类进行数据操作,xmltool<t>泛型类将数据集序列化为.xml文档,rootcollection<t>类封装数据集
- worker类
提供成员方法void dowork<t>()、list<t> getlist<t>()、静态成员方法staticdowork<t>(),代码如下:
1 public class worker
2 {
3 public worker()
4 {
5 }
6
7 public void dowork<t>()
8 {
9 type t = typeof(t);
10 console.writeline("get class: {0}", t.name);
11 propertyinfo[] properties = t.getproperties();
12 foreach (propertyinfo property in properties)
13 {
14 console.writeline("\tproperty.name: " + property.name + "\tproperty.membertype: " + property.propertytype);
15 }
16 }
17
18 public static void staticdowork<t>()
19 {
20 type t = typeof(t);
21 console.writeline("get class: {0}", t.name);
22 propertyinfo[] properties = t.getproperties();
23 foreach (propertyinfo property in properties)
24 {
25 console.writeline("\tproperty.name: " + property.name + "\tproperty.membertype: " + property.propertytype);
26 }
27 }
28
29 public list<t> getlist<t>()
30 {
31 console.writeline("generate list for [{0}]", typeof(t).name);
32 return new list<t>()
33 {
34 activator.createinstance<t>(),
35 activator.createinstance<t>()
36 };
37 }
38 } -
xmltool<t>类
1publicclass xmltool<t>
2 {
3publicstaticvoid xmlserialize_save(list<t> needserializedlist, string xmldirpath, string xmlfilename)
4 {
5 rootcollection<t> collection = new rootcollection<t>();
6 collection.itemlist = needserializedlist;
7if (!directory.exists(xmldirpath))
8 directory.createdirectory(xmldirpath);
9using (system.io.filestream stream = new system.io.filestream(xmlfilename, system.io.filemode.create))
10 {
11 system.xml.serialization.xmlserializer serializer = new system.xml.serialization.xmlserializer(collection.gettype());
12 serializer.serialize(stream, collection);
13 }
14 }
15 } -
rootcollection<t>类:
1 [serializable]
2 public class rootcollection<t>
3 {
4 public rootcollection()
5 {
6 itemlist = new list<t>();
7 }
8
9 private list<t> itemlist;
10
11 public list<t> itemlist
12 {
13 get { return itemlist; }
14 set { itemlist = value; }
15 }
16 } - mockclasslib工程:提供baseentity、apple、cat和person类
-
baseentity类:抽象类,负责初始化类成员
1 public abstract class baseentity
2 {
3 public baseentity()
4 {
5 initiawithnull();
6 }
7
8 private void initiawithnull()
9 {
10 type type = this.gettype();
11 propertyinfo[] properties = type.getproperties();
12 string[] propnames = new string[properties.length];
13 dictionary<string, propertyinfo> propnametoinfo = new dictionary<string, propertyinfo>();
14 for (int i = 0; i < properties.length; i++)
15 {
16 propnames[i] = properties[i].name;
17 propnametoinfo.add(propnames[i], properties[i]);
18 }
19
20 foreach (string propname in propnames)
21 {
22 string proptype = propnametoinfo[propname].propertytype.name;
23
24 object value = null;
25 if (nullvalue.keys.contains(proptype))
26 value = nullvalue[proptype];
27
28 type.getproperty(propname).setvalue(this, value, null);
29 }
30 }
31
32 private static readonly dictionary<string, object> nullvalue = new dictionary<string, object>()
33 {
34 { "string", string.empty },
35 { "datetime", datetime.minvalue},
36 { "decimal", decimal.minvalue}
37 };
38 } -
apple、cat和person类:测试类,继承于baseentity
1 public class apple : baseentity
2 {
3 public string color { get; set; }
4 }
5
6 public class cat : baseentity
7 {
8 public string type { get; set; }
9 }
10
11 public class person : baseentity
12 {
13 public int id { get; set; }
14 public string name { get; set; }
15 }
main工程的program的main方法中,一般情况下,调用worker的泛型方法来处理测试类的话,可以写为:
worker worker = new worker();
worker.dowork<apple>();
worker.dowork<cat>();
worker.dowork<person>();
但是,如果mockclasslib中需要处理的类型非常多时,这样显示调用必然是不灵活的,应当怎样向泛型方法dowork<t>()的尖括号中动态传入类型呢?
考虑代码:
//load assembly
assembly mockassembly = assembly.loadfrom("mockclasslibrary.dll");
type[] typearray = mockassembly.gettypes();
//create instance of worker
worker worker = new worker();
foreach(type curtype in typearray)
{
worker.dowork<curtype>(); //error
}
可以看到,type类型的实例是无法直接传入泛型方法的尖括号中的,t要求显式指明类型名。
下面通过反射方式来获取泛型方法,并创建特定类型的泛型方法。
- 对于非静态方法:public void dowork<t>()
对于非静态方法,调用methodinfo.invoke(object, object[])时,第一个参数需要指明泛型方法的所有者(即这里创建的worker对象),第二个参数为泛
型方法的参数列表,dowork<t>()没有输入参数,所以设为null
//create an instance of worker
worker worker = new worker();
//get type of worker
type workertype = typeof(worker);
//get generic method
methodinfo doworkmethod = workertype.getmethod("dowork");
//invoke dowork<t> with different type
foreach (type curtype in typearray)
{
if (curtype.isclass && !curtype.isabstract)//filter baseentity
{
methodinfo curmethod = doworkmethod.makegenericmethod(curtype);
curmethod.invoke(worker, null);//member method,use instance
}
}
- 对于静态方法:public static void staticdowork<t>()
不同于非静态方法,这里直接反射的类静态方法,所以invoke()的第一个参数设为null
//get type of worker
worker worker = new worker();
//get generic method
methodinfo staticdoworkmethod = workertype.getmethod("staticdowork");
//invoke staticdowork<t>
foreach (type curtype in typearray)
{
if (curtype.isclass && !curtype.isabstract)
{
methodinfo curmethod = staticdoworkmethod.makegenericmethod(curtype);
curmethod.invoke(null, null);//static method
}
}
- 对于有返回值的非静态方法:public list<t> getlist()
如同动态调用dowork<t>()方法一样,只是在处理返回值时,可以使用下面的方法
1 ilist templist = (ilist)curmethod.invoke(worker, null);
2 //or
3 ienumerable templist = (ienumerable)curmethod.invoke(worker, null);
- 对于泛型类:xmltool<t>
下面要使用泛型类xmltool<t>的静态方法public static void xmlserialize_save(list<t> list, string dirpath, string filename)方法。
首先应通过反射构造出指定类型的泛型类xmltool<t>,再反射出其中的xmlserialize_save方法并使用。
1 //use generic class
2 type xmltooltype = typeof(xmltool<>).makegenerictype(curtype);
3
4 //get method
5 methodinfo savemethod = xmltooltype.getmethod("xmlserialize_save");
6
7 //invoke
8 savemethod.invoke
9 (
10 null, //static method
11 new object[] { resultlist, @"c:\", @"c:\test_" + curtype.name + ".xml" }
12 );
program-->main()方法的全部代码:
1 namespace retrieveunknownclass
2 {
3 class program
4 {
5 static void main(string[] args)
6 {
7 //load assembly
8 assembly mockassembly = assembly.loadfrom("mockclasslibrary.dll");
9 type[] typearray = mockassembly.gettypes();
10
11 //create instance of worker
12 type workertype = typeof(worker);
13 worker worker = new worker();
14
15 #region member method
16
17 console.writeline(">>>>>>>>>use generic method:");
18 methodinfo doworkmethod = workertype.getmethod("dowork");
19
20 //invoke dowork<t>
21 foreach (type curtype in typearray)
22 {
23 if (curtype.isclass && !curtype.isabstract)
24 {
25 methodinfo curmethod = doworkmethod.makegenericmethod(curtype);
26 curmethod.invoke(worker, null);//member method,use instance
27 }
28 }
29
30 #endregion
31
32 #region static method
33
34 console.writeline("\r\n>>>>>>>>>use static generic method:");
35 methodinfo staticdoworkmethod = workertype.getmethod("staticdowork");
36
37 //invoke staticdowork<t>
38 foreach (type curtype in typearray)
39 {
40 if (curtype.isclass && !curtype.isabstract)
41 {
42 methodinfo curmethod = staticdoworkmethod.makegenericmethod(curtype);
43 curmethod.invoke(null, null);//static method
44 }
45 }
46
47 #endregion
48
49 #region get a list & serialize it to xml file with generic
50
51 console.writeline("\r\n>>>>>>>>>get list by generic method:");
52 methodinfo getlistmethod = workertype.getmethod("getlist");
53
54 foreach (type curtype in typearray)
55 {
56 if (curtype.isclass && !curtype.isabstract)
57 {
58 methodinfo curmethod = getlistmethod.makegenericmethod(curtype);
59 //generate list
60 ilist resultlist = (ilist)curmethod.invoke(worker, null);
61 //show list
62 showlist(resultlist);
63 //use generic class
64 type xmltooltype = typeof(xmltool<>).makegenerictype(curtype);
65 methodinfo savemethod = xmltooltype.getmethod("xmlserialize_save");
66
67 savemethod.invoke
68 (
69 null, //static method
70 new object[] { resultlist, @"c:\", @"c:\test_" + curtype.name + ".xml" }
71 );
72 }
73 }
74
75 console.writeline("serialization completed...\r\n");
76 #endregion
77 }
78
79 public static void showlist(ilist list)
80 {
81 console.writeline("type of list: {0}\r\ncount of current list: {1}\r\ntype of item in list: {2}\r\n",
82 list.gettype(),
83 list.count,
84 list[0].gettype());
85 }
86 }
87 }
上一篇: vue项目打包webpack体积优化
下一篇: 罗生门是什么意思?各种罗生门事件盘点