一、为什么要使用反射
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
二、反射机制的原理
审查元数据并收集关于它的类型信息的能力。
元数据是一种二进制信息,用以对存储在公共语言运行库可移植可执行文件 (PE) 文件或存储在内存中的程序进行描述。将您的代码编译为 PE 文件时,便会将元数据插入到该文件的一部分中。
元数据以非特定语言的方式描述在代码中定义的每一类型和成员。元数据存储以下信息:
三、反射的作用
反射通常具有以下用途:
-
使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。
-
使用 Module 了解如下的类似信息:包含模块的程序集以及模块中的类等。您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
-
使用 ConstructorInfo 了解以下信息:构造函数的名称、参数、访问修饰符(如public 或private)和实现详细信息(如 abstract 或virtual)等。使用 Type 的 GetConstructors 或 GetConstructor 方法来调用特定的构造函数。
-
使用 MethodInfo 了解以下信息:方法的名称、返回类型、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用 Type 的 GetMethods 或 GetMethod 方法来调用特定的方法。
-
使用 FieldInfo 了解以下信息:字段的名称、访问修饰符(如 public 或private)和实现详细信息(如static)等;并获取或设置字段值。
-
使用 EventInfo 来了解如下的类似信息:事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。
-
使用 PropertyInfo 来了解如下的类似信息:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。
-
使用 ParameterInfo 来了解如下的类似信息:参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。
-
当您在一个应用程序域的仅反射上下文中工作时,请使用 CustomAttributeData 来了解有关自定义属性的信息。使用 CustomAttributeData,您不必创建属性的实例就可以检查它们。
-
System.Reflection.Emit 命名空间的类提供了一种特殊形式的反射,使您能够在运行时构造类型。
-
反射也可用于创建称作类型浏览器的应用程序,它使用户能够选择类型,然后查看有关选定类型的信息。
-
反射还有其他一些用途。JScript 等语言编译器使用反射来构造符号表。System.Runtime.Serialization 命名空间中的类使用反射来访问数据并确定要持久保存的字段。System.Runtime.Remoting 命名空间中的类通过序列化来间接地使用反射。
以上摘自http://msdn.microsoft.com/zh-cn/library/f7ykdhsy%28v=vs.80%29
四、一些小知识
1、类型(Type) 对象是什么
比如 object x; x是对象,object就是它的类型,在程序中如何描述类型这个概念呢?就是Type(System.Type)。要获取某个类的类型可以用typeof()操作符
object a;object b;
DataTable t;
Type aType = typeof(object);Type bType = typeof(object);tType = typeof(DataTable);
aType==bType!=tType;
2、程序集(Assembly)
就是你IDE生成的.exe或.dll文件的运行时就叫程序集。所有的代码都在程序集中。你可以通过Assembly.Load()系列函数动态加载程序集(这一步是动态+载代码的前提,因为所有的代码都在程序集中)。
3、动态加载
我们普通调用代码是: 对象名.方法名(参数列表);
class a{
void func(int x){}
public static void Main(string[] args)
{
//创建对象
a a1 = new a();
//调用函数
a1.func(1);
}
}
用反射动态调用代码是
//加载程序集
System.Reflection.Assembly asm = Assembly.LoadFile(assemblyPath);
//获取类型
Type aType = asm.GetType( "名字空间.类名 ");
//获取没有参数的构造函数
System.Reflection.ConstructorInfo conn = t.GetConstructor(new Type[0]);
//调用没有参数的构造函数,Invoke返回object它其是a类
object a1 = conn.Invoke(new object[0]);
//获取参数类型为int,函数名为func的方法
MethodInfo method = t.GetMethod( "func ",new Type[]{typeof(int)});
//在a1上调用func方法,参数为1
method.Invoke(a1,new object[]{1});
动态调用(后一种方法)比静态调用更复杂,而且效率大概低20倍(网上有个哥们好像测试过)。只有在特殊的时候才调用动态加载动态调用---比如,你的主程序启动的时候子模块还没有,要根据登陆信息下载子模块代码并调用子模块代码,就只能用动态+载来实现了.
4、元数据
.net生成的IL代码中标明了在这个(exe,dll)文件中所有的class(类) method(方法)Attribute(属性) Property(类属性)的签名和调用方法信息,这些信息就叫做元数据。所谓的Reflection反射,就是利用元数据,可以了解到某个assembly(基本等同文件)中的class。。。。。 (就是上面那一串咚咚)信息和调用方法。
5、.net framework
在.net framework中反射类基本都在System.Reflection中。System.Type是反射的核心类.
与它相关的还有System.Attribute命名空间。
五、实例
1、Assembly的使用
Assembly是一个包含来程序的名称,版本号,自我描述,文件关联关系和文件位置等信息的一个集合。在.net框架中通过Assembly类来支持,该类位于System.Reflection下,物理位置位于:mscorlib.dll。我们可以通过Assembly的信息来获取程序的类,实例等编程需要用到的信息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
People people;//这种使用方式可以获取相应类中的变量、方法等元素
string assemblyName = @"ConsoleApplication5";
string strongClassName = @"ConsoleApplication5.Male";//可以把这里的声明看作是路径:命名空间下的类名
people = (People)Assembly.Load(assemblyName).CreateInstance(strongClassName);//这里也可以使用Male或者Female变量来获取相应的属性及方法
Console.WriteLine(people.name);
//Assembly ass = Assembly.Load(assemblyName);//这种使用方式可以获取程序的名称,版本号,自我描述,文件关联关系和文件位置等信息的一个集合
//Assembly ass1 = Assembly.LoadFrom(@"D:\work\myTest\myTest\bin\myTest.dll");
获取程序集显示名称
//Console.WriteLine(ass1.FullName);
获取程序集中定义的类型
//Type[] types = ass.GetTypes();
//foreach (Type t in types)
//{
// Console.WriteLine(t.FullName);
//}
Console.ReadKey();
}
}
class People
{
public string name;
}
class Male : People
{
public Male()
{
name = "你好";
}
public string sex = "男";
public string hello()
{
return "我是"+sex+"生";
}
}
class Female : People
{
public Female()
{
name = "Hello";
}
public string sex = "女";
public string hello()
{
return "我是" + sex + "生";
}
}
}
2、Module的使用
3、ConstructorInfo 的使用
继续试用上面的例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Assembly ass = Assembly.Load("ConsoleApplication5");
Assembly ass1 = Assembly.LoadFrom(@"D:\work\myTest\myTest\bin\myTest.dll");
Type type = ass.GetType("ConsoleApplication5.People");
//将得到的类型传给一个新建的构造器类型变量
ConstructorInfo constructor = type.GetConstructor(new Type[0]);
//使用构造器对象来创建对象
object obj = constructor.Invoke(new Object[0]);
//输出对象类型
Console.WriteLine(obj);
}
}
}
4、MethodInfo 的使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MethodInfoInvokeDemo
{
public class ReflectTest
{
public void MethodWithNoParaNoReturn()
{
Console.WriteLine("不带参数且不返回值的方法");
}
public string MethodWithNoPara()
{
Console.WriteLine("不带参数且有返回值的方法");
return "MethodWithNoPara";
}
public string Method1(string str)
{
Console.WriteLine("带参数且有返回值的方法");
return str;
}
public string Method2(string str, int index)
{
Console.WriteLine("带参数且有返回值的方法");
return str + index.ToString();
}
public string Method3(string str, out string outStr)
{
outStr = "bbbb";
Console.WriteLine("带参数且有返回值的方法");
return str;
}
public static string StaticMethod()
{
Console.WriteLine("静态方法");
return "cccc";
}
}
class Program
{
static void Main(string[] args)
{
Type type = typeof(ReflectTest);
object reflectTest = Activator.CreateInstance(type);
//不带参数且不返回值的方法的调用
MethodInfo methodInfo = type.GetMethod("MethodWithNoParaNoReturn");
methodInfo.Invoke(reflectTest, null);
Console.WriteLine();
//不带参数且有返回值的方法的调用
methodInfo = type.GetMethod("MethodWithNoPara");
Console.WriteLine(methodInfo.Invoke(reflectTest, null).ToString());
Console.WriteLine();
//带参数且有返回值的方法的调用
methodInfo = type.GetMethod("Method1", new Type[] { typeof(string) });
Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "测试" }).ToString());
Console.WriteLine();
//带多个参数且有返回值的方法的调用
methodInfo = type.GetMethod("Method2", new Type[] { typeof(string), typeof(int) });
Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "测试", 100 }).ToString());
//Console.WriteLine();
//methodInfo = type.GetMethod("Method3", new Type[] { typeof(string), typeof(string) });
//string outStr = "";
//Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "测试", outStr }).ToString());
Console.WriteLine();
//静态方法的调用
methodInfo = type.GetMethod("StaticMethod");
Console.WriteLine(methodInfo.Invoke(null, null).ToString());
Console.ReadKey();
}
}
}