反射理解(转)

来源:互联网 发布:机器人算法 编辑:程序博客网 时间:2024/06/04 19:00

1 反射

反射是很多类库在实现一些很炫的功能的时候会常用的技术(比如ORM等)。我们需要动态的使用类型的

时候就需要很灵活的运用反射的技术。

2反射的原理

反射是一种可以获取类型的结构,包括所有成员,动态创建对象并可以动态的操作对象的技术。

我们通过CLR的支持可以在运行时完成一些动态的操作。反射在很多时候配合多态,可以替代switch等结构。

.NET的CLR在编译的时候会把类型的结构等信息生成元数据和代码一起编译在程序集里,

然后通过.NET所提供的System.Refrection命名空间下的各种类去操作。

3  反射的基本操作

反射的操作方式很多,我们主要介绍几种反射简单的使用方法,这些操作都是在实际项目开发过程当中比较常用的。

在进行反射操作的时候,首先需要得到要操作类型的信息,这些信息通过System.Type类型来封装。

获取一个类的Type 对象有下面的3种方法:

3.1通过对象的GetType()方法

每个对象都会继承由Object,,而且都重写了Object所提供的GetType()方法,这些方法能够正确的

返回对象真实类型的System.Type对象。

3.2通过typeof()操作符

如果没有对象怎么获取一个类型的Type呢?我们可以通过typeof(类型)的方式来获取。

4通过Type类的GetType()方法

还有一种方式是通过Type的静态方法GetType来获取,这里需要类型的完全限定名,即从类型所

属的根命名空间开始的完全名称,比如Type类就是System.Type。

这三种方式前两种比较方便所以用的比较多,我们在不同的情况下需要选择正确的方式去获取。

比如如果通过字符串参数传递一个类的名称,或者通过配置文件设定的类的名称使用

Type.GetType()方法。如果是已经传递进来一个对象,那么不管参数是什么类型,

比如参数是Object类型的,这表明任何引用类型的对象都可以传递进来,这个时候使用第一种方式,

直接执行对象的GetType()方法,就可以返回正确的类型。而Type的GetType方法限制比较大,

因为这个方法只能在程序集中搜索类型,所以对于多程序集的程序来说使用上有点麻烦。

得到类型的信息后就可以通过Type类型深入进去开始获取类型内部的信息。Type有很多GetXXX的

方法,我们看名字都能很直接的看出来是获取什么的。这里我们可以获取类内部的很多结构,

包括属性、方法、字段等等。这些结构都用定义在System.Refrection下的各种对应的包装类

给定义出来,我们可以通过相关的包装类获取这些结构的信息。也可以直接操作这些结构,

比如给属性赋值,获取属性的值等等。

我们通过PropertyInfo类来做例子,其它的同理可得。如果详细说明可以另写一本书了,

所以这里点到为止,起码能让你初窥门径了。

假设我们有如下一个类:

class A

{

   private int _Data;

   public int Data

   {

       get { return _Data; }

       set { _Data = value; }

   }

   private string _Str;

   public string Str

   {

       get { return _Str; }

       set { _Str = value; }

   }

}

我们可以通过以下代码来得到一个A类型的对象的所有属性的名称:

Type a = typeof(A);

PropertyInfo[] PropertysInA = a.GetProperties();

foreach (PropertyInfo Property in PropertysInA)

{

   Console.WriteLine(Property.Name);

}

这里PropertyInfo类型的对象就是包含属性信息的类型了,我们在这里可以通过Property变量来操作属性。

我们假设有个A类型的对象,我们通过反射的方式来给他的属性赋值:

void SetObjectValue(object AObject, string PropertyName, string Value)

{

   Type TypeOfA = AObject.GetType();

   PropertyInfo Property = TypeOfA.GetProperty(PropertyName);

   Property.SetValue(AObject, Value, null);

}

同理,,我们也可以用GetValue方法从属性获取值。SetValue()的第三个参数为null,是因为要反射的属性

也包括索引类的属性,所以第三个个参数是用来来传递参数的值。一般的属性就为null就行了。

如果我们只知道一个类型的名字,如何创建它的对象呢?这里我们需要通过System下的一个类型来实现。

这个类叫Activator,它是专门用来实例化对象的。我们可以通过他的静态方法CreateInstance(),

参数可用Type类型的对象比如创建A类型的对象:

A a = (A)Activator. CreateInstance(typeof(A));

这里返回的是Object类型的对象,如果要赋值需要强制转换,当然这里也可以用as操作来转换:

A a= Activator. CreateInstance(typeof(A)) as A;

还有一个泛型的方法可以通过传递类型参数来避免强制转换,关于泛型我们在后面泛型的部分会详细介绍,

这里只看这个方法的例子:

A a = Activator. CreateInstance<A>();

反射是很强大的,我们可以通过它很灵活的去操作对象。但是反射也是很邪恶的,带来灵活的代价就是效率。

经过测试我们发现,直接操作对象和通过反射操作对象的效率相差200倍,所以接下来我们就来谈谈如何正确使用反射。

5 正确使用反射

 

反射产生的性能问题是一个很大的难题,所以我们应该尽量避免滥用反射。但是有的时候避免不了的

话也只有去面对了,比如很多模板引擎和ORM都是没有办法避免频繁使用反射的。

因为反射得来的对象都是很昂贵的,所以我们要尽量重复利用,比如使用缓存将对象缓存起来。

如果不需要对象尽量不要用反射创建对象。我们这里限于篇幅不能铺开叙述,所以只介绍一种通过接口来提供反射性能的方法。

比如有一系列的对象都是用来操作数据库的,有的专门用来操作SQL Server,有的用来操作Access。

我们获取配置里的字符串通过反射去获取相应的实例来操作数据库。这里最笨的方法就是通过

Activator获取对象后,通过MethodInfo去调用相应的方法,这里得到对象就是个很昂贵的操作了,

每次调用都用反射,效率太低了。为了提高效率,我们通过接口来将这些对象的相同操作封装起来:

interface IDAL

{

   void Select();

   void Update();

   void Delete();

   void Insert();

}

那么每一次通过Activator得到对象后都赋值给IDAL定义的对象:

IDAL Accessor = Activator. CreateInstance(Type.GetType("SQLServer")) as IDAL;

这样子以后调用方法都不需要通过反射了,直接通过Accessor.XXXX就能操作了。这样子的效率就比每

一次都用反射去调用方法要快得多。

这个Accessor对象是通过Activator得到的所以很“昂贵”。我们最好尽量把这个对象缓存起来,

下一次直接通过缓存使用,这样子可以大大的提高效率。

原创粉丝点击