第二十三章 程序集加载和反射

1. 程序集加载

Assembly.Load()
这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用 这个方法,他的执行效率比LoadFrom要高很多,而且不会造成重复加载的问题

使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:
⑴如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。         
⑵如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的<codebase>元素指定的URL来查找
⑶如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
   假设你的应用程序目录是C:AppDir,<probing>元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll则CLR将按照如下顺序定位程序集
          C:AppDirAssemblyName.dll
          C:AppDirAssemblyNameAssemblyName.dll
          C:AppDirPath1AssemblyName.dll
          C:AppDirPath1AssemblyNameAssemblyName.dll
如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!
2 Assembly.LoadFrom()
这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,获取其中的程序集版本,语言文化,公钥标记等信息,把他们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集 会被认为是应用程序的一部分,如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。 这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。

LoadFrom还可以接收一个URL,从网上下载程序集.
3   Assembly.LoadFile()
这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集!并且可以多次加载一个相同的程序集导一个AppDomain中.
结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!

4. Assembly.ReflectionOnlyLoadFrom  Assembly.ReflectionOnlyLoad

只通过反射分析程序集的源数据,不希望程序集中的任何代码被执行,可以使用此方法.

程序集是只允许加载,不允许卸载的.

可以将项目引用的DLL嵌入到自己的项目Exe中,这样发布的时候就可以只部署自己的EXE文件.在运行时,CLR可能会找不到这些依赖的DLL(如果不是MS本身的).可以再AppDomain的ResolveAssembly事件中进行处理,读取并加载自己的 资源文件里的DLL.

2.  反射

有关反射性能优化等,参考

http://www.cnblogs.com/heyuquan/archive/2012/07/11/2586594.html

http://www.cnblogs.com/fish-li/archive/2013/02/18/2916253.html

Assembly.GetExecutingAssembly().GetExportedTypes 获取此程序集中定义的公共类型,这些公共类型在程序集外可见

构造类型实例方式:

Activator.CreateInstance创建类型的一个实例,该类型由指定的 System.ActivationContext 对象指定

Activator.CreateComInstanceFrom 使用命名的程序集文件和默认构造函数,来创建其名称在指定的远程域中指定的类型的实例.返回ObjectHandle

AppDomain.CurrentDomain.CreateInstance CreateInstanceAndUnwrap在当前Domain中创建.

type.InvokeMember

type.GetConstructor().Invoke

泛型使用type.MakeGenericType

type.DeclaringType 获取用来声明当前的嵌套类型或泛型类型参数的类型.获取的是定义这个类型的类型.

public class Test
{
    public void DoSomething()
    {
    }
}

var type = typeof(Test);
var methods = type.GetMethods();
foreach (var method in methods) {
    Console.WriteLine(method.Name + "  " + method.DeclaringType);
}

image

BindingFlags筛选返回的成员种类

 1 // 摘要: 
 2 //     不指定绑定标志。 
 3 Default = 0, 
 4 // 
 5 // 摘要: 
 6 //     指定当绑定时不应考虑成员名的大小写。 
 7 IgnoreCase = 1, 
 8 // 
 9 // 摘要: 
10 //     指定只应考虑在所提供类型的层次结构级别上声明的成员。 不考虑继承成员。 
11 DeclaredOnly = 2, 
12 // 
13 // 摘要: 
14 //     指定实例成员将包括在搜索中。 
15 Instance = 4, 
16 // 
17 // 摘要: 
18 //     指定静态成员将包括在搜索中。 
19 Static = 8, 
20 // 
21 // 摘要: 
22 //     指定公共成员将包括在搜索中。 
23 Public = 16, 
24 // 
25 // 摘要: 
26 //     指定非公共成员将包括在搜索中。 
27 NonPublic = 32, 
28 // 
29 // 摘要: 
30 //     指定应返回层次结构上的公共静态成员和受保护的静态成员。 不返回继承类中的私有静态成员。 静态成员包括字段、方法、事件和属性。 不返回嵌套类型。 
31 FlattenHierarchy = 64, 
32 // 
33 // 摘要: 
34 //     指定要调用一个方法。 它不能是构造函数或类型初始值设定项。 
35 InvokeMethod = 256, 
36 // 
37 // 摘要: 
38 //     指定“反射”应该创建指定类型的实例。 调用与给定参数匹配的构造函数。 忽略提供的成员名。 如果未指定查找类型,将应用 (Instance |Public)。 
39 //     调用类型初始值设定项是不可能的。 
40 CreateInstance = 512, 
41 // 
42 // 摘要: 
43 //     指定应返回指定字段的值。 
44 GetField = 1024, 
45 // 
46 // 摘要: 
47 //     指定应设置指定字段的值。 
48 SetField = 2048, 
49 // 
50 // 摘要: 
51 //     指定应返回指定属性的值。 
52 GetProperty = 4096, 
53 // 
54 // 摘要: 
55 //     指定应设置指定属性的值。 对于 COM 属性,指定此绑定标志与指定 PutDispProperty 和 PutRefDispProperty 是等效的。 
56 SetProperty = 8192, 
57 // 
58 // 摘要: 
59 //     指定应调用 COM 对象的 PROPPUT 成员。 PROPPUT 指定使用值的属性设置函数。 如果属性同时具有 PROPPUT 和 PROPPUTREF,而且需要区分调用哪一个,请使用 
60 //     PutDispProperty。 
61 PutDispProperty = 16384, 
62 // 
63 // 摘要: 
64 //     指定应调用 COM 对象的 PROPPUTREF 成员。 PROPPUTREF 指定使用引用而不是值的属性设置函数。 如果属性同时具有 PROPPUT 
65 //     和 PROPPUTREF,而且需要区分调用哪一个,请使用 PutRefDispProperty。 
66 PutRefDispProperty = 32768, 
67 // 
68 // 摘要: 
69 //     指定提供参数的类型必须与对应形参的类型完全匹配。 如果调用方提供一个非空 Binder 对象,则“反射”将引发异常,因为这意味着调用方正在提供的 
70 //     BindToXXX 实现将选取适当的方法。 
71 ExactBinding = 65536, 
72 // 
73 // 摘要: 
74 //     未实现。 
75 SuppressChangeType = 131072, 
76 // 
77 // 摘要: 
78 //     返回其参数计数与提供参数的数目匹配的成员集。 此绑定标志用于所带参数具有默认值的方法和带变量参数 (varargs) 的方法。 此标志应只与 System.Type.InvokeMember(System.String,System.Reflection.BindingFlags,System.Reflection.Binder,System.Object,System.Object[],System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[]) 
79 //     一起使用。 
80 OptionalParamBinding = 262144, 
81 // 
82 // 摘要: 
83 //     在 COM 互操作中用于指定可以忽略成员的返回值。 
84 IgnoreReturn = 16777216, 
View Code

3. 设计支持加载项的应用程序(如CAD的二次开发,插件)

托管可扩展性框架(MEF)

构建可扩展的应用程序,接口是中心.

跨程序集使用类型时,需要关注程序集的版本控制问题.

步骤:

1.创建宿主SDK程序集,定义接口,作为通信机制.

2.加载项开发人员使用SDK中定义的类型.

3. 创建一个单独的”宿主应用程序”,引用SDK中定义的类型.

原文地址:https://www.cnblogs.com/zhangliming/p/3495225.html