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 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券