(9)程序集的加载和反射

=============C#.Net 篇目录==============

 

一、程序集的加载

程序集是 .NET Framework 应用程序的构造块;程序集构成了部署、版本控制、重复使用、激活范围控制和安全权限的基本单元。

绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。根据此过程是发生在编译时还是运行时分为:

a)         静态绑定:在生成时,编译器在程序集清单的元数据中记录静态引用。

b)         动态绑定:由于调用各种方法而动态构造的,EG Assembly.Load 方法。

程序集如何加载请参见下面链接:

1、 《(5)CLR 运行时探测程序集引用的步骤》

2、 《(6)程序集加载上下文》

3、 《(7)动态程序集加载Load()》

 

二、程序集的反射

         字段、构造器、方法、属性、事件和嵌套类型都可以被定义成一个类型的成员。FCL(Framework Class Library—框架类库)定义了一个抽象基类 MemberInfo ,封装了一组所有类型成员都通用的属性。

clip_image001[4] 

对“动态绑定”的程序集进行成员绑定的规则可通过 Binder 类重写,作为搜索成员方法的参数传入;如果此参数传null,则使用默认绑定规则进行成员绑定同“静态绑定”。

clip_image003[4]

1.         区分DeclaringType ReflectedType

a)         ReflectedType 获取用于获取 MemberInfo 的此实例的类对象。返回的类对象总是执行反射的Type起点。

b)         DeclaringType 获取声明该成员的类。

clip_image005[4]

2.         GetCustomAttributes()

运行时自定义特性反射模型

-------System.DLL

public class DescriptionAttribute : Attribute

{ ……}

-------System.Web.DLL

internal class MyDescriptionAttribute : DescriptionAttribute

{ ……}

public class LocalizationExtenderProvider

{

    [MyDescriptionAttribute(...)]

    public CultureInfo GetLanguage(...)

    { ……}

}

如果运行时尝试使用GetCustomAttributes(Type type) 为附加到 GetLanguage 方法的公共自定义特性类型 DescriptionAttribute 检索自定义特性,则该运行时将执行下列操作:

a)         运行时检查 GetCustomAttributes(Type type) DescriptionAttribute 类型参数是否为公共的,判断是否可见和访问。

b)         运行时检查从 DescriptionAttribute 派生的用户定义类型 MyDescriptionAttribute System.Web.DLL 程序集(它在该程序集中附加到 GetLanguage() 方法)内是否可见和可以访问。

c)         运行时检查 MyDescriptionAttribute 的【构造函数】是否在 System.Web.DLL 程序集中可见和可以访问。

a)        运行时调用带有自定义特性参数的 MyDescriptionAttribute 的构造函数,然后将新对象返回给调用方。(“只反射上下问”因为不能执行代码,所以需使用 CustomAttributeData 类访问自定义特性)

 

三、获取Type对象的引用

1)         C# typeof 操作符获得某个类型的 Type 对象,typeof操作符相对其他获取type方式能获得更高的性能,操作符用于获取“早期绑定”的类型信息。

2)         Object.GetType 方法返回表示【实例类型】的 Type 对象。可以直接用 o1.GetType()==o2.GetType() 来判断对象是否属于同一个类型。

3)         System.Reflection.Assembly 对象可先Load尚未加载的程序集,再获取 Type 对象,包括 Assembly.GetType()Assembly.GetTypes() Assembly.GetExportedTypes()

4)         Module.GetTypesModule.GetType Module.FindTypes 方法返回 Type 对象,这些对象表示在某个模块中定义的类型。第一个方法可用于获得模块中定义的所有公共类型和私有类型的 Type 对象的数组。(可通过 Assembly.GetModule()Assembly.GetModules () Type.Module 属性来获得 Module 的实例。)

5)         Type类方法:

a)         静态 Type.GetType 方法可从已加载的程序集中获取 Type 对象,该对象表示由其完全限定名指定的类型。

b)         FindInterfaces 方法返回某个类型所支持的接口类型的筛选后的列表。

c)         GetElementType 方法返回表示元素的 Type 对象。

d)         GetInterfaces GetInterface 方法返回表示某个类型所支持的接口类型的 Type 对象。

e)         GetTypeArray 方法返回表示任意一组对象所指定的类型的 Type 对象数组。这些对象用 Object 类型的数组指定。

f)          GetTypeFromProgID GetTypeFromCLSID 方法是为 COM 互操作而提供的。这些方法返回表示 ProgID CLSID 所指定的类型的 Type 对象。

g)         GetTypeFromHandle 方法是为COM互操作而提供的。此方法返回表示类句柄所指定的类型的 Type 对象。

h)         MakeGenericType 方法返回 Type 对象,该对象表示构造泛型类型,如果该对象的 ContainsGenericParameters 属性返回 true,则该类型为开放构造类型,否则为封闭构造类型。只能实例化封闭的泛型类型。

i)           MakeArrayTypeMakePointerType MakeByRefType 方法返回 Type 对象,这些对象分别表示指定类型的数组、指向指定类型的指针以及引用参数的类型(在 C# 中为 ref)。

四、构造类型的实例

1)         System.Activator CreateInstance() 静态方法       

注意CLR不要求值类型定义任何构造函数,所以想在不调用构造器的情况下创建值类型的一个实例,必须调用 Activator.CreateInstance() 并且是传入Type 或传入TypeBoolean参数的重载。

2)         System.Activator CreateInstanceFrom() 静态方法

         通过指定程序集文件名、类型名等参数,内部会根据传入的程序集文件名调用AssemblyLoadFrom()加载程序集。返回值不是对新对象的一个引用,而是ObjectHandle对象引用(派生自MarshalByRefObject),必须调用ObjectHandleUnwarp()方法解包对象。

3)         System.AppDomain的实例方法

CreateInstance()CreateInstanceAndUnwrap()CreateInstanceFrom()CreateInstanceFromAndUnwrap()。同Activator类方法行为类似。

4)         System.Type InvokeMember() 实例方法

5)         System.Reflection.ConstructorInfo类的Invoke() 实例方法

6)         ArrayCreateInstance() 静态方法,创建System.Array及其派生类的实例

7)         Delegate CreateDelegate() 静态方法,创建委托(System.MulticastDelegate派生类型)的实例

五、反射的性能

反射机制在提供运行时发现并使用编译时还不了解的类型及其成员功能时,也存有其缺点:

1.反射会造成编译时无法保证类型安全性

由于反射要严重依赖字符串,所以会丧失编译时的类型安全性(即:编译成功,运行报错)。

2.反射速度慢

a)搜索:使用 System.Reflection 命名空间中的类型扫描程序集的元数据时,反射要不断的执行字符串的搜索。通常,搜索时不区分大小写的比较,这会更进一步影响性能。

b)调用:使用反射调用一个成员时。比如调用方法,首先必须将实参打包(pack)成一个数组;在内部,反射必须将这些实参解包(unpack)到线程栈上。此外,在调用方法前,CLR必须检查实参具有正确的数据类型。最后,CLR必须确保调用者有正确的安全权限来访问被调用的成员。

六、调用类型的成员

1.         Type InvokeMember() 实例方法  (出于性能和扩展考虑,推荐直接使用第二种缓存方式)

通过指定:成员名称、BindingFlags、要在其上调用成员的Object、调用成员的参数数组等参数进行成员调用

                   该方法会执行两个操作。

a)         绑定成员:选择要调用的一个恰当的成员

b)         调用:实际调用成员

2.         一次绑定,多次调用

a)         绑定成员:先通过 Type 的如 GetFields()GetConstructors()GetMethods()GetProperties()GetEvents() 等方法获取并缓存这个对象引用,该对象的类型提供了直接访问特定成员的方法。

b)         调用:

成员类型

用于调用成员的方法

FieldInfo

调用GetValue获取字段的值

调用SetValue设置字段的值

PropertyInfo

调用GetValue调用属性的get访问器方法

调用SetValue调用属性的set访问器方法

EventInfo

调用AddEventHandler调用事件的add访问器方法

调用RemoveEventHandler调用事件的remove访问器方法

ConstructorInfo

调用Invoke构造类型的一个实例,并调用一个构造器

MethodInfo

调用Invoke调用类型的一个方法

 

 

                 《反射机制》系列:

                                 (1)程序集基础知识

                                 (2)强名称程序集与数字证书

                                 (3)程序集加载 Assembly类

                                 (4)绑定程序集配置策略

                                 (5)CLR 运行时探测程序集引用的步骤

                                 (6)程序集加载上下文

                                 (7)动态程序集加载Load()

                                 (8)程序集反射 Type 类

                                 (9)程序集的加载和反射

    参考书籍:ClR via C#(第3版)

你可能感兴趣的:(反射)