1..NET反射的概述
.NET反射是审查元数据并动态收集关于它的类型信息的能力。
应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域。这些域在拥有相同应用程序范围的对象周围形成了确定边界。
这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。
我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。
反射的层次模型:
2..NET反射的作用和应用
(1).可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型;
(2).应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射;
(3).反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能;
(4).在.NET中实现工厂模式会使用反射较多;
(5).在JavaScript等语言编译器使用反射来构造符号表;
(6).反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息;
(7).System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段;
(8).System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。
3..NET反射的性能
使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需 要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。 所以尽量不要使用反射进行编程,对于打算编写一个动态构造类型(晚绑定)的应用程序,可以采取以下的几种方式进行代替:
(1).通过类的继承关系
让该类型从一个编译时可知的基础类型派生出来,在运行时生成该类 型的一个实例,将对其的引用放到其基础类型的一个变量中,然后调用该基础类型的虚方法。
(2).通过接口实现
在运行时,构建该类型的一个实例,将对其的引用放到其接口类型的一个变量中,然后调用该接口定义的虚方法。
(3).通过委托实现
让该类型实现一个方法,其名称和原型都与一个在编译时就已知的委托相符。 在运行时先构造该类型的实例,然后在用该方法的对象及名称构造出该委托的实例,接着通过委托调用你想要的方法。这个方法相对与前面两个方法所作的工作要多一些,效率更低一些。
提高反射的性能:反射的性能损失主要来源于比较类型、遍历成员、调用成员三种情形,其中比较类型耗时最小。 调用成员耗时最多,所以尽量减少采用成员动态调用等反射方式可以提高应用程序性能。除此之外,采取后期绑定、避免将反射方法放到循环内产生放大效应等办法均可提升反射性能。
4..NET反射常用类型
System.reflection命名空间下常用类型,允许你反射(解析)这些元数据表的代码和反射相关的命名空间(可以通过这几个命名空间访问反射信息):
(1).System.Reflection.Assembly
使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。可以使用Assembly.Load和Assembly.LoadFrom方法动态地加载程序集。
(2).System.Reflection.Module
使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 (3).System.Reflection.ConstructorInfo
使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
(4).System.Reflection.MethodInfo
使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5).System.Reflection.FieldInfo
使用FieldInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6).System.Reflection.EventInfo
使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7).System.Reflection.PropertyInfo
使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8).System.Reflection.ParameterInfo
使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。
(9).System.Type
System.Type类是一个抽象类,代表公用类型系统中的一种类型。这个类使您能够查询类型名、类型中包含的模块和名称空间、以及该类型是一个数值类型还是一个引用类型。 System.Type类使您能够查询几乎所有与类型相关的属性,包括类型访问限定符、类型是否、类型的COM属性等等。
(10).System.Activator
Activator类支持动态创建.NET程序集和COM对象。可以通过CreateComInstanceFrom、CreateInstance、CreateInstanceFrom、GetObject四个静态方法加载COM对象或者程序集,并能创建指定类型的实例。
使用注意:
(1).Load方法:极力推荐的一种方法,Load 方法带有一个程序集标志并载入它,Load 将引起CLR把策略应用到程序集上。 先后在全局程序集缓冲区,应用程序基目录和私有路径下面查找该程序集,如果找不到该程序集系统抛出异常。
(2).LoadFrom方法:传递一个程序集文件的路径名(包括扩展名),CLR会载入您指定的这个程序集,传递的这个参数不能包含任何关于版本号的信息,区域性,和公钥信息,如果在指定路径找不到程序集抛出异常。
(3).LoadWithPartialName:永远不要使用这个方法,因为应用程序不能确定再在载入的程序集的版本。该方法的唯一用途是帮助那些在.Net框架的测试环节使用.net 框架提供的某种行为的客户,这个方法将最终被抛弃不用。
5..NET反射示例Demo
(1).代码结构
(2).TestClassLib类库的Arithmetic类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { /// <summary> /// 定义示例接口Iwel /// </summary> public interface Iwel { string Print(); } /// <summary> /// 构造测试数据类 /// </summary> public class Arithmetic:Iwel { //定义私有变量 private int numOne; private int numTwo; #region 构造函数测试 //不带参数的构造函数 public Arithmetic() { } //带参数构造函数,初始化numOne和numTwo public Arithmetic(int intNumOne, int intNumTwo) { this.numOne = intNumOne; this.numTwo = intNumTwo; } #endregion #region 属性测试 //公共属性访问NumOne public int NumOne { get { return numOne; } set { numOne = value; } } //公共属性 访问NumTwo public int NumTwo { get { return numTwo; } set { numTwo = value; } } #endregion #region 方法测试 /// <summary> /// 私有非静态不带参数的方法 /// </summary> /// <returns></returns> private string Add() { return "Add()方法是一个私有不带参数的方法"; } /// <summary> /// 私有静态带参数无返回值的方法 /// </summary> /// <param name="intNumOne"></param> private static void Multiplcation(int intNumOne) { Console.WriteLine("Multiplication()方法是一个私有不带参数无返回值的静态方法"); } /// <summary> /// 私有非静态带参数的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> private void Subtration(int intNumOne, int intNumTwo) { string strMeesage = "{0}-{1}={2} Subtration(int intNumOne, int intNumTwo)方法是一个私有带参数的方法"; Console.WriteLine(strMeesage, intNumOne, intNumTwo, intNumOne - intNumTwo); } /// <summary> /// 公有非静态不带参数的方法 /// </summary> /// <returns></returns> public void Write() { Console.WriteLine( "Write() 是一个共有的不带参数无返回值的方法"); } /// <summary> /// 公有静态带参数的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> public static string Multiplcation(int intNumOne, int intNumTwo) { string strMessage = "{0}*{1}={2} Multiplcation(int intNumOne,int intNumTwo)方法是一个公有的静态带参数的方法"; strMessage = string.Format("{0}*{1}={2}", intNumOne, intNumTwo, intNumOne * intNumTwo); return strMessage; } /// <summary> /// 公有非静态带参数的方法 /// </summary> /// <param name="intNumOne"></param> /// <param name="intNumTwo"></param> /// <returns></returns> public string Add(int intNumOne,int intNumTwo) { string strMessage = string.Format("{0}+{1}={2}", intNumOne, intNumTwo, intNumOne + intNumTwo); return strMessage; } #endregion /// <summary> /// 集成接口方法 /// </summary> /// <returns></returns> public string Print() { return " Print() 继承接口方法"; } } }
(3).TestClassLib类库的TestClass类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClassLib { public class TestClass { //模拟测试 public class Dialog : DialogInvoker { } public virtual string TestMethod(string pp) { return pp; } } }