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 }
復制代碼

 

       相關文章:

 

出處: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM