在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考。
主程序加载另一个程序集,将其中的所有类取出,然后对这些类分别调用泛型类或泛型方法。控制台程序解决方案如下:
- 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
() 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 () 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 GetList () 30 { 31 Console.WriteLine("Generate List for [{0}]", typeof(T).Name); 32 return new List () 33 { 34 Activator.CreateInstance (), 35 Activator.CreateInstance () 36 }; 37 } 38 } - XMLTool<T>类
1publicclass XMLTool
2 { 3publicstaticvoid XmlSerialize_Save(List needSerializedList, string xmlDirPath, string xmlFileName) 4 { 5 RootCollection collection = new RootCollection (); 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
3 { 4 public RootCollection() 5 { 6 itemList = new List (); 7 } 8 9 private List itemList; 10 11 public List 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
PropNameToInfo = new Dictionary (); 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 NullValue = new Dictionary () 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 }
- BaseEntity类:抽象类,负责初始化类成员
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(); //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 DoWorkwith 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 StaticDoWorkforeach (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 DoWork21 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 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 }