一、概念:
1、反射机制是一种运行时获取类(Type对象)和动态调用对象的成员的机制。
a.可以获取有关已加载的程序集和在其中定义的类型(如类、接口和值类型)的成员信息;
b.可以使用反射在运行时创建指定类的对象,以及调用和访问这些对象的成员。
这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。
2、C#编译原理:
(1)C#生成exe文件后,放在bin目录中。bin是binary(二进制)的缩写,但在bin目录中实际放的是IL文件,而不是二进制文件。
*添加引用的真实作用:
a.修改了项目的工程文件.csproj(工程文件),在里面添加了被引用的项目所在的位置。
b.那么编译器在生成 宿主程序 (A引用B,则A是宿主) 的时候,会先编译生成 引用的项目,如果引用的项目的程序集生成成功,则把程序集文件 复制到 宿主程序的 bin目录下;
c.在宿主程序中可以直接访问 被引用的项目里的命名空间和类了!(其实是在运行的时候,宿主程序主动将被引用程序加到了宿主程序的应用程序域中了。)
(2)JIT将IL中间语言编译为二进制机械语言,但不是一次性全部编译完,而是用什么就编译什么,未编译前就是一堆字符串。
(3)按F5其实是完成了两个步骤:1.生成exe(F6),2.双击exe时系统用framework运行。
*注册表记录哪种后缀名要用哪种程序打开。程序安装时会在注册表登记这些信息。exe默认用操作系统运行,但是因为操作系统不懂IL,于是交给FW运行。FW运行时会先找Main函数。
*默认配置文件(App.config)不会编译到exe里去,改动后不用重新生成,但要重启程序,因为只在打开时读一次。
(4)exe下载到内存后叫assembly。class对应内存中type的对象。
二、程序集(Assembly) .exe .dll
1.概念
2.方法:
(1)AppDomain.CurrentDomain.GetAssemblies();//获得程序所有的Assembly
*应用程序域(AppDomain):一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点开始,沿着对象激活的序列的任何位置)。应用程序域有助于将在一个应用程序中创建的对象与在其他应用程序中创建的对象隔离,以使运行时行为可以预知。在一个单独的进程中可以存在多个应用程序域。应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。
(2)Assembly.GetExecutingAssembly();//获取正在执行的 程序集对象
(3)Assembly.Load(@"F:\DAL\bin\Debug\DAL");//这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用这个方法,因为效率最高!
a.如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。
b.如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的<codebase>元素指定的URL来查找
c.如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!
(4)Assembly.LoadFrom(@"F:\DAL\bin\Debug\DAL.dll");//通过程序集文件路径 会载入dll文件及其引用的其他dll,不能用于加载标识相同但路径不同的程序集。
(5)Assembly.LoadFile();//用来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
(6)通过Type.Assembly获取Type所在的程序集对象。
MODEL.Cat cat = new MODEL.Cat();
Type catType = cat.GetType();
Assembly assModel = catType.Assembly;
(7)Assembly.GetExecutingAssembly().Location//获得到Exe的全路径
三、Type类
1.概念:
2.获得Type对象的方法:
(1)通过类获得Type:Type t = typeof(Person)
(2)通过对象获得类的Type:Type t = p.GetType()
(3)调用Assembly的GetTypes()方法可以得到Assembly中定义的所有的类型。
(4)调用Assembly的GetExportedTypes方法可以得到Assembly中定义的所有的public类型。
(5)调用Assembly的GetType(name)方法可以得到Assembly中定义的全名为name的类型信息。
Assembly.GetExecutingAssembly().GetType("MyNameSpace.Dog");
* 类放在内存就是type对象:Name:类名,FullName:命名空间.类名
3.Type类的方法:
(1)bool IsAssignableFrom(Type c):判断当前的类型的变量是不是可以接受c类型变量的赋值。(c类型变量是否实现当前类型)
Type typeCat=typeof(Cat);//类
Type typeIBark=typeof(IBark);//接口
Console.WriteLine(typeIBark.IsAssignableFrom(typeCat));//判断类是否实现接口
(2)bool IsInstanceOfType(object o):判断对象o是否是当前类的实例(当前类可以是o的类、父类、接口)
(3)bool IsSubclassOf(Type c):判断当前类是否是类c的子类。类的事,没有接口的事。如果当前类是
(4)MemSettingsProvider,c是ISettingsProvider,则返回false。
4.动态调用成员
(1)调用Type的GetProperty方法可以根据属性名获得属性对象PropertyInfo,
主要成员:CanRead、CanWrite、PropertyType
属性类型:SetValue、GetValue:读取值,设置值,第一个参数是实例对象,因为set、get要针对具体实例,最后一个参数null。
type.GetProperty(“Age”).SetValue(p1, 30, null),
(2)调用Type的GetMethod方法可以根据方法名获得方法对象MethodInfo,调用MethodBase 的Invoke方法就可以调用方法,第一个参数是实例对象,第二个参数是参数数组,如果没有参数设置为null。
(3)Type类还有很多方法:GetConstructor(获得构造函数)、GetEvent(获得事件)、GetProperties(获得所有属性)等。
5.动态创建对象
(1)CreateInstance是创建对应type类型的object对象,要使用要装换成相应的对象
(2)Activator.CreateInstance(Type t)会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果类没有无参构造函数就会报错。(应用:通过配置文件动态创建对象。)
(3)调用有参构造函数,有几个参数就传几个:
Dog d=Activator.CreateInstance(t,"Japan") as Dog;
四、应用:
1、简单反射工厂模式
*为什么使用配置文件?
因为配置文件可以不编译到程序集中,随时可以进行方便的修改,程序集在运行的时候可以读取配置文件里配置的信息,从而影响程序内部的运行。
2、所谓静态,是编译器的玩意,通过反射可以破解!
class Program
3、调用private方法,
Person p1 = new Person();
Type type = p1.GetType();
//BindingFlags.Instance表示是实例方法,即不是static方法
//MethodInfo mHaha =
//mHaha.Invoke(p1, null);