您的位置:首页 > 编程语言 > C#

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

2012-03-13 14:16 525 查看
在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考。

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

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

Worker类

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

public class Worker
{
public Worker()
{
}

public void DoWork<T>()
{
Type t = typeof(T);
Console.WriteLine("Get Class: {0}", t.Name);
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("\tproperty.Name: " + property.Name + "\tproperty.MemberType: " + property.PropertyType);
}
}

public static void StaticDoWork<T>()
{
Type t = typeof(T);
Console.WriteLine("Get Class: {0}", t.Name);
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("\tproperty.Name: " + property.Name + "\tproperty.MemberType: " + property.PropertyType);
}
}

public List<T> GetList<T>()
{
Console.WriteLine("Generate List for [{0}]", typeof(T).Name);
return new List<T>()
{
Activator.CreateInstance<T>(),
Activator.CreateInstance<T>()
};
}
}


[b]XMLTool<T>类[/b]

publicclass XMLTool<T>
{
publicstaticvoid XmlSerialize_Save(List<T> needSerializedList, string xmlDirPath, string xmlFileName)
{
RootCollection<T> collection = new RootCollection<T>();
collection.ItemList = needSerializedList;
if (!Directory.Exists(xmlDirPath))
Directory.CreateDirectory(xmlDirPath);
using (System.IO.FileStream stream = new System.IO.FileStream(xmlFileName, System.IO.FileMode.Create))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(collection.GetType());
serializer.Serialize(stream, collection);
}
}
}


RootCollection<T>类:

[Serializable]
public class RootCollection<T>
{
public RootCollection()
{
itemList = new List<T>();
}

private List<T> itemList;

public List<T> ItemList
{
get { return itemList; }
set { itemList = value; }
}
}


MockClassLib工程:提供BaseEntityAppleCatPerson

BaseEntity类:抽象类,负责初始化类成员

1     public abstract class BaseEntity
{
3         public BaseEntity()
{
InitiaWithNull();
}

8         private void InitiaWithNull()
{
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
12             string[] PropNames = new string[properties.Length];
Dictionary<string, PropertyInfo> PropNameToInfo = new Dictionary<string, PropertyInfo>();
14             for (int i = 0; i < properties.Length; i++)
{
PropNames[i] = properties[i].Name;
PropNameToInfo.Add(PropNames[i], properties[i]);
}

20             foreach (string propname in PropNames)
{
22                 string proptype = PropNameToInfo[propname].PropertyType.Name;

24                 object value = null;
25                 if (NullValue.Keys.Contains(proptype))
value = NullValue[proptype];

type.GetProperty(propname).SetValue(this, value, null);
}
}

32         private static readonly Dictionary<string, object> NullValue = new Dictionary<string, object>()
{
{ "String", String.Empty },
{ "DateTime", DateTime.MinValue},
{ "Decimal", Decimal.MinValue}
};
}


AppleCatPerson类:测试类,继承于BaseEntity

1     public class Apple : BaseEntity
{
3        public string Color { get; set; }
}

6     public class Cat : BaseEntity
{
8        public string Type { get; set; }
}

11     public class Person : BaseEntity
{
13        public int ID { get; set; }
14        public string Name { get; set; }
}


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>()方法一样,只是在处理返回值时,可以使用下面的方法

IList tempList = (IList)curMethod.Invoke(worker, null);
//Or
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方法并使用。

//Use Generic Class
Type xmlToolType = typeof(XMLTool<>).MakeGenericType(curType);

//Get method
MethodInfo saveMethod = xmlToolType.GetMethod("XmlSerialize_Save");

//Invoke
saveMethod.Invoke
(
null, //Static method
new object[] { resultList, @"c:\", @"c:\Test_" + curType.Name + ".xml" }

);


Program-->Main()方法的全部代码:

namespace RetrieveUnknownClass
{
class Program
{
static void Main(string[] args)
{
//Load assembly
Assembly mockAssembly = Assembly.LoadFrom("MockClassLibrary.dll");
Type[] typeArray = mockAssembly.GetTypes();

//Create instance of Worker
Type workerType = typeof(Worker);
Worker worker = new Worker();

#region Member method

Console.WriteLine(">>>>>>>>>Use Generic Method:");
MethodInfo doWorkMethod = workerType.GetMethod("DoWork");

//Invoke DoWork<T>
foreach (Type curType in typeArray)
{
if (curType.IsClass && !curType.IsAbstract)
{
MethodInfo curMethod = doWorkMethod.MakeGenericMethod(curType);
curMethod.Invoke(worker, null);//Member method,use instance
}
}

#endregion

#region Static method

Console.WriteLine("\r\n>>>>>>>>>Use Static 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
}
}

#endregion

#region Get A List & Serialize It to Xml File With Generic

Console.WriteLine("\r\n>>>>>>>>>Get List By Generic Method:");
MethodInfo getListMethod = workerType.GetMethod("GetList");

foreach (Type curType in typeArray)
{
if (curType.IsClass && !curType.IsAbstract)
{
MethodInfo curMethod = getListMethod.MakeGenericMethod(curType);
//Generate List
IList resultList = (IList)curMethod.Invoke(worker, null);
//Show List
ShowList(resultList);
//Use Generic Class
Type xmlToolType = typeof(XMLTool<>).MakeGenericType(curType);
MethodInfo saveMethod = xmlToolType.GetMethod("XmlSerialize_Save");

saveMethod.Invoke
(
null, //Static method
new object[] { resultList, @"c:\", @"c:\Test_" + curType.Name + ".xml" }
);
}
}

Console.WriteLine("Serialization Completed...\r\n");
#endregion
}

public static void ShowList(IList list)
{
Console.WriteLine("Type of list: {0}\r\nCount of current list: {1}\r\nType of item in list: {2}\r\n",
list.GetType(),
list.Count,
list[0].GetType());
}
}
}


[b]相关文章:[/b]

关于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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: