反射原理及简介
- 格式:pdf
- 大小:131.35 KB
- 文档页数:4
反射原理及简介
⼀.什么是反射
Reflection,中⽂翻译为反射。这是.Net中获取运⾏时类型信息的⽅式,
.Net的应⽤程序由⼏个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,⽽反射提供⼀种编程的⽅式,让程序员可以在程序运
⾏期获得这⼏个组成部分的相关信息,
例如:Assembly类可以获得正在运⾏的程序集信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:⽅法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调
⽤之。
MethodInfo包含⽅法的信息,通过这个类可以得到⽅法的名称、参数、返回值等,并且可以调⽤之。诸如此类,
还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。
⼆. 关于程序集和命名空间的关系
很多⼈对这个概念可能还是很不清晰,对于合格的.Net程序员,有必要对这点进⾏澄清。
程序集是.NET应⽤程序执⾏的最⼩单元,编译出来的.dll和.exe都是程序集。 程序集和命名空间的关系不是⼀⼀对应,也不互相包含,⼀个
程序集⾥⾯可以有多个命名空间,⼀个命名空间也可以在多个程序中存在,这样说可能有点模糊,举个例⼦:
程序集A包含两个命名空间:
namespace N1
{
public class AC1 {…}
public class AC2 {…}
}
namespace N2
{
public class AC3 {…}
public class AC4{…}
}
程序集B包含两个命名空间:
namespace N1
{
public class BC1 {…}
public class BC2 {…}
}
namespace N2
{
public class BC3 {…}
public class BC4{…}
}
这两个程序集中都有N1和N2两个命名空间,⽽且各声明了两个类,这样是完全可以的,然后我们在⼀个应⽤程序中引⽤程序集A,那么在
这个应⽤程序中,我们能看到N1下⾯的类为AC1和AC2,N2下⾯的类为AC3和AC4。
接着我们去掉对A的引⽤,加上对B的引⽤,那么我们在这个应⽤程序下能看到的N1下⾯的类变成了BC1和BC2,N2下⾯也⼀样。 如果我们
同时引⽤这两个程序集,那么N1下⾯我们就能看到四个类:AC1、AC2、BC1和BC2。
到这⾥,我们可以清楚⼀个概念了,命名空间只是说明⼀个类型是那个族的,⽐如有⼈是汉族、有⼈是回族;⽽程序集表明⼀个类型住在哪
⾥,⽐如有⼈住在北京、有⼈住在上海;那么北京有汉族⼈,也有回族⼈,上海有汉族⼈,也有回族⼈,这是不⽭盾的。
上⾯我们说了,程序集是⼀个类型居住的地⽅,那么在⼀个程序中要使⽤⼀个类,就必须告诉编译器这个类住在哪⼉,编译器才能找到它,
也就是说必须引⽤该程序集。
那么如果在编写程序的时候,也许不确定这个类在哪⾥,仅仅只是知道它的名称,就不能使⽤了吗?答案是可以,这就是反射了,就是在程
序运⾏的时候提供该类型的地址,⽽去找到它。
三.为什么使⽤反射
有⼈会有疑问,程序所⽤的类既然可以事先写好,那么为什么还要在程序运⾏的时候去⽣成,这样岂不是浪费系统资源。存在就是合理的,
既然微软给我们开发这项技术,肯定是这个这个东西有需求,举个简单的例⼦:
现在要开发⼀个报表打印模块,有的企业要求数据以Excel报表,有的企业要求打印⽔晶报表等等,这个时候我们就可以先定义⼀个接⼝,
任何报表打印⽅法都必须实现这个接⼝:
public interface IReport
{
void StartPrint();
}
我们通过配置⽂件可以加载对应的读取报表的类型,因为报表打印类都实现了IReport的接⼝,所以都可以通过反射的⽅式强转为该接⼝类
型,这样就可以实现⽆需修改底层代码就可以实现打印不同的数据报表,符合我i们程序设计的开闭原则,这个就是反射最经典的应⽤。
public static class Factory
{
//【1】读取配置⽂件
static string reportType = ConfigurationManager.AppSettings["ReportType"].ToString();
//【2】使⽤反射创建实现接⼝类的对象并以接⼝类型返回
public static IReport ChooseReportType()
{
return (IReport)Assembly.Load("UseFactory").CreateInstance("UseFactory." + reportType);
}
}
四.如何使⽤反射获取类型
获取类信息有两种⽅式:
第⼀种⽅式,得到实例化对象。 这个时侯我仅仅是得到这个实例对象,得到的⽅式也许是⼀个object的引⽤,也许是⼀个接⼝的引⽤,但是
我并不知道它的确切类型,我需要了解,那么就可以通过调⽤System.Object上声明的⽅法GetType来获取实例对象的类型对象,⽐如在某个
⽅法内,我需要判断传递进来的参数是否实现了某个接⼝,如果实现了,则调⽤该接⼝的⼀个⽅法。
public void Progress(object o)
{
Type objType = o.GetType();
if (objType.GetInterface("ITest") !=null)
{
//调⽤该接⼝的⽅法
}
}
第⼆种获取类型的⽅法是通过Type.GetType以及Assembly.GetType⽅法,但是在使⽤该⽅法时我们需要注意⼀些问题点。在程序集A.dll中
需要反射程序集B.dll中的类型。如果使⽤稍有不慎,就会产⽣运⾏时错误。例如使⽤Type.GetType("BNameSpace.ClassName")在程序集
A.dll获取程序集B.dll中的类型,就会返回Null。
关于跨程序集的反射,有两点需要注意:
1、如果使⽤typeof,编译能通过,则跨程序集的反射⼀定可以正常运⾏。可以说,typeof是⽀持强类型的。⽐如
Type supType = typeof(BNameSpace.SubSpace.Class);
如果当前程序集没有添加对EnterpriseServerBase.dll的引⽤,则编译会报错。
2、如果使⽤Type.GetType来进⾏反射的话,情况就复杂些。这是因为Type.GetType是⾮强类型的。Type.GetType的参数是⼀个string为类
型的完全限定名,如果当string表⽰的⽬标类型不在当前程序集中,则运⾏时Type.GetType会返回null。解决的办法是:⾸先加载⽬标程序
集,然后再使⽤Assembly.GetType⽅法来获取类型。如:
Assembly asmb = Assembly.LoadFrom("EnterpriseServerBase.dll") ;
Type supType = asmb.GetType("EnterpriseServerBase.DataAccess.IDBAccesser") ;
注意:当使⽤Type.GetType的时候,即使你添加了对EnterpriseServerBase.dll的引
⽤,Type.GetType("EnterpriseServerBase.DataAccess.IDBAccesser")也会返回null,这是因为Type.GetType只会在当前程序集中进⾏类型
搜索。
5.如何根据类型动态创建对象
第⼀种⽅式
public class Example
{
static void Main()
{
// Create an instance of the StringBuilder type using
// Activator.CreateInstance.
Object o = Activator.CreateInstance(typeof(StringBuilder));
// Append a string into the StringBuilder object and display the
// StringBuilder.
StringBuilder sb = (StringBuilder) o; sb.Append("Hello, there.");
Console.WriteLine(sb);
// Create an instance of the SomeType class that is defined in this
// assembly.
System.Runtime.Remoting.ObjectHandle oh =
Activator.CreateInstanceFrom(Assembly.GetEntryAssembly().CodeBase,
typeof(SomeType).FullName);
// Call an instance method defined by the SomeType type using this object.
SomeType st = (SomeType) oh.Unwrap();
st.DoSomething(5);
}
}
例⼆:根据有参数的构造器创建对象
namespace TestSpace
{
public class TestClass
{
private string _value;
public TestClass(string value)
{
_value=value;
}
}
}
Type t = Type.GetType(“TestSpace.TestClass”);
Object[] constructParms = new object[] {“hello”}; //构造器参数
TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms);
把参数按照顺序放⼊⼀个Object数组中即可
第三种⽅式:
假如我们并没有加载对应.cs⽂件的程序集,那么我们该如何简洁的创建对象类型呢:
object objCal =Assembly.LoadFrom("CalDLL.dll").CreateInstance("CalDLL.Calculator");
是不是很简洁,当然我们也可以先获取对象的类型,再⽤Activator.CreateInstance 来创建对象,不过稍显⿇烦:
Assembly objAssembly = Assembly.LoadFrom("CalDLL.dll");
Type objType = objAssembly.GetType("CalDLL.Calculator");
object objCal = Activator.CreateInstance(objType);
这样也能达到相同的效果。
6.如何获取⽅法以及动态调⽤⽅法
应⽤反射动态调⽤⽅法的⽰例如下:
using System;
using System.Reflection;