反射初步
首先来认识一下反射,咱们每次咱认识一个新东西的时候,需要了解这么一个情况,这个东西是啥,它的作用是啥,我学他有啥用,搞明白了这三件事就算可以了.
说实话,楼主学了.NET也得一年多了吧,一次反射也没用到过,当然楼主涉世未深,也不了解反射,最近几天楼主和大家一起研究研究反射的东西.
反射初步
假设你在开发一个据点预订系统,在订单处理过程中需要标示酒店预订的状态:未提交,已提交,已取消,受理中,已退回,已订妥,已过期.此时,很多开发人员可能会这样建表:
BookingStatus | |
Id | Status |
1 | 未提交 |
2 | 已提交 |
3 | 已取消 |
4 | 受理中 |
5 | 已退回 |
6 | 已订妥 |
7 | 已过期 |
这个表的作用只是用来查询,不会提供给用户增删改的操作.在程序中获取这个表的数据时,经常是这种情况:需要根据预定状态对订单进行筛选,此时通常的做法是使用一个下拉菜单(DropDownList),通过ADO.NET获取这个表的数据,然后再将DropDownList的Test设为Status字段,Value设为Id字段.
采用这种做法时,会有下面这2个问题:
(1).如果还存在比如航班预定之类的状态,那么需要在数据库中创建很多类似的小表,从而导致数据库表的数目态度.
(2).使用DropDownList等控件获取表内容时,经常需要连接到数据库进行查询,这可能会影响性能.
同时注意到以下三点:
(1).这个表不会改动
(2).这表一般不会在数据库联合产讯中使用到.
(3).在应用程序中,这表经常作为DropDownList或者其他控件的数据源.
因为状态在定义好以后就不改变了,那么使用数据库先得多余了,咱们可以使用数组来表示预定状态,这样可以从数据库中删除这个表了.这个数组的内容可能会是这样:
string[] BookingStatus = { "NoUse","未提交","已取消","已退回","已订妥","已过期"};
数组是从下标0开始的,所以咱们把下标0的元素设置成别的东西.
使用数组就能把上面的两个问题都给解决了.既不用建表,也不用连接到数据库.
这样的话使用一个订单类HotelOrder,其属性StatusId代表订单状态,这样的话确实有一点不太方便,当需要更新订单状态的状态值时,除非记住所有状态的数字值,否则就要去查看BookingStatus数组的定义,然后根据状态值在数组中的位置来为对象的属性赋值.
假设系统的操作人员分为两组,一组为消费者,负责下订单;一组为操作员,负责处理订单.对于消费者来说,只要看到未提交,已提交,已取消,已订妥四个状态,对应的值分别是1,2,3,6.对于操作员来说,需要看到全部的订单状态.
如果继续使用数组来定义状态,就会发下你个问题:默认将订单状态值与数组的索引一一对应了起来.当这种一一对应的关系被打破时,使用数组的方法就失效了,因为如果不利用数组索引,就没有额外的地方去存储状态的数字值,这个时候可使用枚举:
public enum BookingStatus
{
未提交=1,
已提交,
已取消,
已订妥=6
}
使用枚举的原因是我们可以让VS的智能提示完全可以起到作用.将枚举绑定到DropDownList下拉列表中,可以绑定到下拉列表的有两种状态:一种是实现了IEnumerable接口普的可枚举类型,比如ArrayList,String[],List<T>;一类是实现了IListSource的数据源,比如DataTable,DataSet.可是,枚举enum是一个基本类型,他不会实现任何接口,显然无法将枚举绑定到DropDownList上.
不管是VS的智能提示,还是修改变量名时的重构功能,都使用了反射的功能.下面向大家展示一个基本的案例,C#中一共有两种类型,一种是值类型,一种是引用类型.声明一个引用类型的变量并实例化类型,会在应用程序堆(Application Heap)上分配空间,创建对象实例,然后将对象实例的内存地址返回给变量,变量保存的是内存地址,实际相当于一个指针;声明一个值类型的实例变量,则会将它分配在线程堆栈(Thread Stack)上,变量本身包含了值类型的所有字段.
现在假设需要比较两个对象是否相等.当比较两个引用类型的变量是否相等时,比较的是这两个变量所指向的是不是堆上的同一个实例,即内存地址是否相同.而比较值类型变量时是否相等时,咋办?因为变量本身就包含了值类型所有的字段(数据),所以在比较时,就需要对两个变量的字段进行逐个的一对一的比较,看看每个字段的值是否相等,如果任何一个字段不相等,就返回false.
进行这样的比较不需要我们自己编写代码,因为所有的值类型都继承自System.ValueType,ValueType继承自System.Object,Object提供了一个方法,用来判断两个对象是否相等.但是ValueType类型覆盖了Object的Equals()方法.当比较两个值类型变量时,会调用继承自ValueType类型的Equals()方法.
那么ValueType中的Equals()方法如何获取值类型的所有字段,并进行一一对比呢?显然,咱们打开Reflector来查看ValueType类的Equals()方法,看看具体是如何实现的:
[SecuritySafeCritical, __DynamicallyInvokable]
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
RuntimeType type = (RuntimeType) base.GetType();
if (((RuntimeType) obj.GetType()) != type)
{
return false;
}
object a = this;
if (CanCompareBits(this))
{
return FastEqualsCheck(a, obj);
}
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++)
{
object obj3 = ((RtFieldInfo) fields[i]).UnsafeGetValue(a);
object obj4 = ((RtFieldInfo) fields[i]).UnsafeGetValue(obj);
if (obj3 == null)
{
if (obj4 != null)
{
return false;
}
}
else if (!obj3.Equals(obj4))
{
return false;
}
}
return true;
}
反射最大的问题就是性能不佳,由此可见,在值类型上调用Equals()方法开销会很大.但是这个例子仅仅是为了说明发射的应用场景.
反射其实是一种宽泛的叫法,它通过System.Reflection命名空间并配合System.Type类,提供了在运行时(Runtime)对类型和类型成员的元数据的访问能力.
reflector下载地址:
http://www.waitalone.cn/reflector-v8-crack-version.html