C#编译后的文件主要由IL代码和元数据组成,元数据为.NET组件提供了丰富的自描述特性,它使得我们可以在代码运行时获知组件中的类型等重要的信息。C#中这是通过一种称作反射(Reflection)的机制来完成的。
动态类型查询
首先创建一个简单的类型:
namespace ReflectionClass
{
public class MyClass
{
#region Property
private int m_Count = 100;
public int Count
{
get
{
return m_Count;
}
set
{
m_Count = value;
}
}
#endregion
#region Method
public void Print()
{
Console.WriteLine("MyClass.Count = {}", Count);
}
#endregion
}
}
编译后可以得到“ReflectionClass.dll”文件,接下来实现查询类型的测试程序:
namespace TestReflection
{
public class App
{
static void Main(string[] args)
{
Type type = typeof(MyClass); //获取MyClass的类型信息
Console.WriteLine("The Type Name : {0}", type.Name); //获取类型的名字
FieldInfo[] fieldArray = type.GetFields(); //获取所有的公有域
Console.Write("The {0} Fields : ", fieldArray.Length);
foreach (FieldInfo field in fieldArray)
{
Console.Write(field.Name + " ");
}
Console.WriteLine();
PropertyInfo[] propertyArray = type.GetProperties(); //获取所有的公有属性
Console.Write("The {0} Properties : ", propertyArray.Length);
foreach (PropertyInfo property in propertyArray)
{
Console.Write(property.Name + " ");
}
Console.WriteLine();
MethodInfo[] methodArray = type.GetMethods(); //获取所有的公有方法
Console.Write("The {0} Methods : ", methodArray.Length);
foreach (MethodInfo method in methodArray)
{
Console.Write(method.Name + " ");
}
}
}
}
编译后执行,可以得到以下输出:
The Type Name : MyClass
The 0 Fields :
The 1 Properties : Count
The 7 Methods : get_Count set_Count Print ToString Equals GetHashCode GetType
在上面的例子中,首先通过 typeof(MyClass)获得MyClass类的类型信息,当然也可以通过创建对象实例,然后调用对象实例的GetType方法来获得(每个类都从 object根类中继承获得此方法)。在拥有了类型信息(变量type)后,便可以获得其类型的名字、该类型含有的公有域、公有属性、公有方法。注意: 这里C#的反射机制只允许获取类型的公有信息,这符合面向对象的封装原则。其中4个方法(GetHashCode、Equals、ToString、GetType)都是继承自object类的公有方法,而方法get_Count 和set_Count则是实现Count属性的“副产物。实际上,System.Type类各种各样的成员使得我们能够获得几乎所有与类型相关的公有信息。在System.Reflection命名空间下的各个类都可以获得各个编程元素较详细的信息,如方法的参数与返回值、域的类型、枚举的各个值等。
动态创建与调用
实际上反射远不止动态地获知组件的类型信息,它还能在获得类型信息的基础上,在代码运行时进行类型的动态创建与方法的动态调用。
namespace TestReflection2
{
public class App
{
static void Main(string[] args)
{
Assembly assemlby = Assembly.LoadFrom("ReflectionClass.dll"); //装载组件
foreach (var type in assemlby.GetTypes())
{
if (type.IsClass && !type.IsAbstract)
{
MethodInfo[] methodArray = type.GetMethods(); //获得类型的公有方法
object obj = Activator.CreateInstance(type); //创建实例(无参构造器)
foreach (var method in methodArray)
{
if (!method.IsAbstract && !method.IsStatic && method.GetParameters().Length == 0)
{
object ret = method.Invoke(obj, null); //调用实例方法
Console.WriteLine("{0}'s Return : {1}", method.Name, ret);
}
}
}
}
}
}
}
编译后执行,可以得到以下输出:
get_Count's Return : 100
MyClass.Count = 100
Print's Return :
ToString's Return : ReflectionClass.MyClass
GetHashCode's Return : 62476613
GetType's Return : ReflectionClass.MyClass
在上面的例子中给出了被动态调用的方法名字和返回值。其中第二行输出的“MyClass.Count = 100”,它是动态调用方法MyClass.Print()的输出。需要指出的是调用的是类型的公有无参数实例方法。给出组件的名字,应用Assembly.LoadFrom,我们便可以动态地装载组件。 Activator.CreateInstance()允许动态地创建类型(这里只通过无参数的构造器来创建),实际上用它创建出来的类型和用“MyClass obj = new MyClass()”创建出来的类型一样。进而,还可以在查询到的成员的基础上,对它们进行动态调用。
另外,还可以用“Assembly.CreateInstance()”创建实例,从本质上讲,“Assembly.CreateInstance()”就是调用的“Activator.CreateInstance()”。