C#动态调用泛型类、泛型方法

您所在的位置:网站首页 静态方法中不能使用类的泛型 C#动态调用泛型类、泛型方法

C#动态调用泛型类、泛型方法

2023-10-17 00:21| 来源: 网络整理| 查看: 265

      在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考。

      主程序加载另一个程序集,将其中的所有类取出,然后对这些类分别调用泛型类或泛型方法。控制台程序解决方案如下:

Main工程:提供Worker类进行数据操作,XMLTool泛型类将数据集序列化为.xml文档,RootCollection类封装数据集 Worker类

       提供成员方法void DoWork()、List GetList()、静态成员方法StaticDoWork(),代码如下:

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类 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类: 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 ItemList12 {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 }1920 foreach (string propname in PropNames)21 {22 string proptype = PropNameToInfo[propname].PropertyType.Name;2324 object value = null;25 if (NullValue.Keys.Contains(proptype))26 value = NullValue[proptype];2728 type.GetProperty(propname).SetValue(this, value, null);29 }30 }3132 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 }1011 public class Person : BaseEntity12 {13 public int ID { get; set; }14 public string Name { get; set; }15 }

 

      Main工程的Program的Main方法中,一般情况下,调用Worker的泛型方法来处理测试类的话,可以写为:

      Worker worker = new Worker();

      worker.DoWork();

      worker.DoWork();

      worker.DoWork();

      但是,如果MockClassLib中需要处理的类型非常多时,这样显示调用必然是不灵活的,应当怎样向泛型方法DoWork()的尖括号中动态传入类型呢?

      考虑代码:

//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()

          对于非静态方法,调用MethodInfo.Invoke(object, object[])时,第一个参数需要指明泛型方法的所有者(即这里创建的worker对象),第二个参数为泛

          型方法的参数列表,DoWork()没有输入参数,所以设为null

//Create an instance of WorkerWorker worker = new Worker();

//Get type of WorkerType workerType = typeof(Worker);

//Get Generic MethodMethodInfo doWorkMethod = workerType.GetMethod("DoWork");

//Invoke DoWork with different Typeforeach (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()

          不同于非静态方法,这里直接反射的类静态方法,所以Invoke()的第一个参数设为null

//Get type of WorkerWorker worker = new Worker();

//Get Generic MethodMethodInfo 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 GetList()

          如同动态调用DoWork()方法一样,只是在处理返回值时,可以使用下面的方法

1 IList tempList = (IList)curMethod.Invoke(worker, null);2 //Or3 IEnumerable tempList = (IEnumerable)curMethod.Invoke(worker, null); 对于泛型类:XMLTool

          下面要使用泛型类XMLTool的静态方法public static void XmlSerialize_Save(List list, string dirPath, string fileName)方法。

          首先应通过反射构造出指定类型的泛型类XMLTool,再反射出其中的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 method11 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 method16 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 instance27 }28 }29 30 #endregion31 32 #region Static method33 34 Console.WriteLine("\r\n>>>>>>>>>Use Static Generic Method:");35 MethodInfo staticDoWorkMethod = workerType.GetMethod("StaticDoWork");36 37 //Invoke StaticDoWork38 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 method44 }45 }46 47 #endregion48 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 List60 IList resultList = (IList)curMethod.Invoke(worker, null);61 //Show List62 ShowList(resultList);63 //Use Generic Class64 Type xmlToolType = typeof(XMLTool).MakeGenericType(curType);65 MethodInfo saveMethod = xmlToolType.GetMethod("XmlSerialize_Save");66 67 saveMethod.Invoke68 (69 null, //Static method70 new object[] { resultList, @"c:\", @"c:\Test_" + curType.Name + ".xml" }71 );72 }73 }74 75 Console.WriteLine("Serialization Completed...\r\n");76 #endregion77 }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 }

 

       相关文章:

关于MackGenericMethod()方法 关于MethodInfo.Invoke()(+2)方法 Overcoming problems with MethodInfo.Invoke of methods with by-reference value type arguments How to: Define a Generic Type with Reflection Emit

 

出处:https://www.cnblogs.com/lichence/archive/2012/03/13/2393758.html

=======================================================================================

我自己的使用:

 

 

=======================================================================================

对泛型进行反射

今天在用反射的时候突然想到,之前从来没有对泛型对象进行反射,故决定尝试一下首先要获取某个泛型类的Type,键入如下代码:

            Type t = Type.GetType("System.Collections.Generic.List");

但是调试发现,t为null,看来类名写的不对,再试试,System.Collections.Generic.List,还是错,再试试System.Collections.Generic.List,还是不对,接连又试了好几个,还是不对,毛了,先看看List的Type究竟是啥,键入如下代码查看:

            List list = new List();

            Type t = list.GetType();

            Console.WriteLine(t.FullName);

结果输出:System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]我就晕,咋是这么个玩意儿?那就再试试

            Type t = Type.GetType("System.Collections.Generic.List`1[System.Int32]");

再次调试,好,这回对了接着我们来调用一下List的Add方法,往里插入一条数据试试

             MethodInfo add = t.GetMethod("Add", new Type[1] { typeof(int) });//找到Add方法

             object list = t.InvokeMember(null,

                 BindingFlags.DeclaredOnly |

                 BindingFlags.Public | BindingFlags.NonPublic |

                 BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });//实例化一个List对象list

             add.Invoke(list, new object[] { 12356 });//调用Add方法,往list中插入一条数据

             Type t2 = list.GetType();

             Console.WriteLine(t2.InvokeMember("Item", BindingFlags.GetProperty, null, list, new object[] { 0 }));//获取list的第一条索引输出

输出结果正确,接下来我又想用反射调用一下泛型的方法,再写入以下代码:

            MethodInfo convert = t2.GetMethod("ConvertAll", BindingFlags.Public | BindingFlags.Instance);//获取ConvertAll泛型方法

            object list2 = Type.GetType("System.Collections.Generic.List`1[System.String]").InvokeMember(null, BindingFlags.DeclaredOnly |

                BindingFlags.Public | BindingFlags.NonPublic |

                BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });//实例化一个List对象list2用以存储类型转换后的数据

            list2 = convert.Invoke(list, new object[] { new Converter(Convert.ToString) });//调用ConvertAll方法

            Console.WriteLine(list2.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, list, new object[] { 0 }));//输出list2的第一个索引值

再次调试,但是很可惜,出现了以下错误:不能对 ContainsGenericParameters 为 True 的类型或方法执行后期绑定操作。这是怎么回事?查阅了以下MSDN,其中给出的MethodInfo.ContainsGenericParameters 属性的解释为:

为调用泛型方法,方法自身的类型参数或任何封闭类型中必须不含泛型类型定义或开放构造类型。进行这种递归确认是很困难的。为方便起见,也为了减少错误,ContainsGenericParameters 属性提供一种标准方法来区分封闭构造方法(可以调用)和开放构造方法(不能调用)。如果 ContainsGenericParameters 属性返回 true,则方法不能调用。

那这样看来是因为我没有将泛型的类型先传进去造成的,再次查阅了一下msdn,发现了MakeGenericMethod方法,该方法

将当前泛型方法定义的类型参数替换为类型数组的元素,并返回表示结果构造方法的 MethodInfo 对象。行,有戏,再次试试,在Invoke前加上如下代码:

 

convert = convert.MakeGenericMethod(typeof(string));

再次调试,OK,正常了,至此,我们用对泛型进行反射调用成功了,完整代码:

 

 

            Type t = Type.GetType("System.Collections.Generic.List`1[System.Int32]");

            MethodInfo add = t.GetMethod("Add", new Type[1] { typeof(int) });

            object list = t.InvokeMember(null,

                BindingFlags.DeclaredOnly |

                BindingFlags.Public | BindingFlags.NonPublic |

                BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });

            add.Invoke(list, new object[] { 12356 });

            Type t2 = list.GetType();

            Console.WriteLine(t2.InvokeMember("Item", BindingFlags.GetProperty, null, list, new object[] { 0 }));

            MethodInfo convert = t2.GetMethod("ConvertAll", BindingFlags.Public | BindingFlags.Instance);

            object list2 = Type.GetType("System.Collections.Generic.List`1[System.String]").InvokeMember(null, BindingFlags.DeclaredOnly |

                BindingFlags.Public | BindingFlags.NonPublic |

                BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });

            convert = convert.MakeGenericMethod(typeof(string));

            list2 = convert.Invoke(list, new object[] { new Converter(Convert.ToString) });

            Console.WriteLine(list2.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, list2, new object[] { 0 }));

 

出处:https://www.cnblogs.com/jordan2009/archive/2013/05/09/3068275.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3