每个.NET程序集除了程序代码外都额外包含了元数据。元数据包含了程序集本身的信息,比如版本号,引用了什么程序集等。元数据也包含了程序集内所有类型的信息,包括其方法、属性和字段。使用.NET反射,你可以在运行时读取这些信息,并且可以动态地调用方法。
让我们来看这样一个例子,它通过使用.NET反射读取和动态调用了一个方法。在这个示例中有一个Demo类,它带有方法Message以被动态调用。Demo类在程序集ReflectionLib.dll中。
namespace Samples.Reflection
{
public class Demo
{
public void Message(string s)
{
System.Console.WriteLine(s);
}
}
}
我们有一个简单的控制台程序,其中有一个Test类,动态地调用了Demo类的Message方法。首先,含有Demo类的程序集通过Assembly类的LoadFrom静态方法来加载入内存,这个程序集的文件名来自于命令行参数,是在启动这个应用程序时传入的。
通过Assembly.LoadFrom方法返回的assembly对象,可以读取其中的元数据。方法GetType会返回一个用于表示Samples.Reflection.Demo的Type对象。如果你想读取此程序集的所有的类型,方法GetTypes会返回一个Type对象的数组。
调用方法t.GetMethod会返回方法自身的信息,表现(represented)于MethodInfo类中。通过MethodInfo对象,你可以读取方法的名称,得到关于参数的信息。通过MethodInfo的Invoke方法,你可以动态地调用此方法。因为实例方法需要在调用时有一个实例存在,所以用Activator.CreateInstance根据从程序集中获得的类型(前面得到的Type对象)创建了一个对象。Invoke方法允许你通过一个对象数组传递任意数量的参数到目标方法。Message方法只需要一个参数,所以传入了一个只包含一个对象的数组到Invoke方法中。
结果是,Message方法在控制台输出了字符串“Test”。
using System;
using System.Reflection;
class Test
{
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("filename needed");
return;
}
string filename = args[0];
Assembly assembly = Assembly.LoadFrom(filename);
Type t = assembly.GetType("Samples.Reflection.Demo");
MethodInfo mi = t.GetMethod("Message");
object o = Activator.CreateInstance(t);
object[] parameters = new object[] {"Test"};
mi.Invoke(o, parameters);
}
}