zoukankan      html  css  js  c++  java
  • 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(" property.Name: " + property.Name + " property.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(" property.Name: " + property.Name + " property.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(" >>>>>>>>>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(" >>>>>>>>>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... ");
    76 #endregion
    77 }
    78
    79 public static void ShowList(IList list)
    80 {
    81 Console.WriteLine("Type of list: {0} Count of current list: {1} Type of item in list: {2} ",
    82 list.GetType(),
    83 list.Count,
    84 list[0].GetType());
    85 }
    86 }
    87 }
    复制代码

           相关文章:

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

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

    我自己的使用:

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

    对泛型进行反射

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

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

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

                List<int> list = new List<int>();

                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<int>的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, nullnullnew object[] { });//实例化一个List<int>对象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, nullnullnew object[] { });//实例化一个List<string>对象list2用以存储类型转换后的数据

                list2 = convert.Invoke(list, new object[] { new Converter<intstring>(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前加上如下代码:

    1. 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, nullnullnew 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, nullnullnew object[] { });

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

                list2 = convert.Invoke(list, new object[] { new Converter<intstring>(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

    您的资助是我最大的动力!
    金额随意,欢迎来赏!
    款后有任何问题请给我留言。

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我。(●'◡'●)

    如果你觉得本篇文章对你有所帮助,请给予我更多的鼓励,求打             付款后有任何问题请给我留言!!!

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【Jack_孟】!

  • 相关阅读:
    数据切分——Atlas介绍
    HDU 5015 233Matrix (构造矩阵)
    Wincc操作数据库SQLSERVER
    UIWebView 设置背景为透明
    29个你必须知道的Linux命令
    【读书笔记】iOS-UIWindow-WindowLevel
    linux下uart应用编程
    Java Web HelloWorld!
    手把手图文教你eclipse下如何配置tomcat
    Tomcat安装及配置教程
  • 原文地址:https://www.cnblogs.com/mq0036/p/15259927.html
Copyright © 2011-2022 走看看