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工程:提供BaseEntityAppleCatPerson
    • 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 }
      复制代码
    • AppleCatPerson类:测试类,继承于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 }
复制代码
posted @ 2018-12-26 09:02 幕三少 阅读(...) 评论(...) 编辑 收藏