CLR 学习笔记:程序集加载和反射(1)

      程序集加载和反射,实现了在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息,创建类型的实例以及访问类型的成员。显现的功能以及效果是十分强大的,比如使用第三方提供的程序集,以及创建动态可扩展应用程序。

程序集加载:

JIT编译器在将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,JIT编译器查看元数据表TypeRef和AssemblyRe,确定对应的程序集。在AssemblyRef表中,包含了构成程序集强名称的各个部分 —名称(无扩展名和路径),版本,语言文化和公钥。StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5

JIT编译器利用这些信息,连接成字符串,然后尝试将一个匹配的程序集加载到AppDomain中。如果是弱命名程序集,则只包含程序集的名称。

常见的加载选择有三种:Assembly.Load, Assembly.LoadFrom, Assembly.LoadFile。

(1)Assembly.Load

在内部,CLR使用System.Reflection.Assembly类的静态方法Load来尝试加载程序集,常用的版本原型:

1 public class Assembly
2 {
3       public static Assembly Load(AssemblyName assemblyRef);
4       public static Assembly Load(String assemblyString);
5 }

首先熟悉下这两个方法的使用,创建个强命名程序集,然后查看强命名信息(SN.exe, Reflector工具, Assembly.GetAssemblyName)

1 Assembly assemblyLoadString = Assembly.Load("StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5");
2 Assembly assemblyLoadRef = Assembly.Load(new AssemblyName("StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5"));

注意:

a.对于强命名程序集,在内部,Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC中查找程序集。如果没有找到,就接着去应用程序的基目录,私有路径子目录和codebase位置中查找

b.如果Load时传递的是一个弱命名程序集,Load就不会向程序集应用一个版本绑定重定向策略,CLR也不会去GAC中查找程序集

c.如果找到指定的程序集,返回的是那个程序集的一个Assembly对象的引用;如果没有找到指定的程序集,抛出System.IO.FileNotFoundException

d.对于需要加载为特定CPU架构生成的程序集,在指定程序集标识时,还可包括一个进程架构部分。

StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5ProcessorArchitecture=MSIL

 CLR允许ProcessorArchitecture取4个值之一:MSIL, x86, IA64, AMD64

(2)Assembly.LoadFrom

使用Load时,它要求你事先掌握构成程序集标识的各个部分。在某些情况下,我们也可以指定一个程序集文件的路径名(包括文件扩展名),获取Assembly对象。

1 Assembly assemblyFromPath = Assembly.LoadFrom(@"E:StrongNameDLL.dll");

对于使用LoadFrom,传入路径名的使用方式,需要了解内部的实现机制,避免误用的情况:

a.在内部,LoadFrom首先会调用System.Reflection.AssemblyName类的静态方法GetAssemblyName。

该方法打开指定的文件,提取AssemblyRef记录项中的程序集标识信息,然后以一个System.Reflection.AssemblyName对象的形式返回这些信息(文件同时关闭)。

b.LoadFrom方法在内部调用Assembly的Load方法,将AssemblyName对象传给它。

c.CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。如果Load找到了匹配的程序集,就会加载它,并返回一个Assembly对象;LoadFrom返回这个值。如果没有找到匹配项,LoadFrom就会加载路径名中的程序集。

(3)Assembly.LoadFile

加载指定路径上的程序集文件。

1 Assembly assemblyFromPath = Assembly.LoadFile(@"E:StrongNameDLL.dll");

注意:

a.可将具有相同标识的一个程序集多次加载到一个AppDomain中

b.CLR不会解析任何依赖性问题

c.必须向AppDomain的AssemblyResolve事件登记,并让事件回调方法显式地加载任何依赖的程序集

三者对比
既然已经对Load,LoadFrom,LoadFile有所了解,那么接着来看看这三者之间的区别与对比。

1.Load和LoadFrom

a.根据LoadFrom的内部实现机制,LoadFrom返回的实际是Load找到的匹配程序集的一个Assembly对象(在找到匹配的程序集的情况下)

b.LoadFrom存在多次打开文件的现象,并且内部还是要走一套Load的逻辑操作,还存在对比情况,所以LoadFrom比Load效率低

c.LoadFrom要求指定路径中包含 FileIOPermissionAccess.Read FileIOPermissionAccess.PathDiscovery WebPermission

d.如果用LoadFrom加载一个程序集,并且probing路径包括具有相同标识但位置不同的程序集,则发生InvalidCastExceptionMissingMethodException或其他意外行为。

2.LoadFrom和LoadFile

a.根据MSDN的解释,可以看出功能实现机制存在区别。

  LoadFrom:已知程序集的文件名或路径,加载程序集。

  LoadFile:加载指定路径上的程序集文件的内容。

b.LoadFrom会加载程序集的依赖项,但是LoadFile不会。

  例如,上面的StrongName.dll存在引用程序集ReferenceDemo.dll。使用LoadFrom,StrongName.dll和ReferenceDemo.dll都会被载入,但是使用LoadFile,ReferenceDemo.dll不会被载入。

c.可以使用LoadFile加载并检查具有相同标识但位于不同路径的程序集,但是LoadFrom不行,如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,仍返回已加载的程序集。

  例如,StrongName.dll存在于两个不同路径下,LoadFile可以正确载入两个程序集;但是LoadFrom如果已经加载了一次StrongName.dll,再一次加载不同路径下的Assembly时,会先检查前面是否已经载入过相同名字的Assembly,最终返回的还是第一次加载的Assembly对象的引用。

总结:

由于LoadFrom具有不少缺点,一般优先考虑使用Load方法加载程序集。而LoadFile则只在有限的情况下使用。

原文地址:https://www.cnblogs.com/ByronsHome/p/3524998.html