程序集加载

加载过程

JIT编译器将IL代码编译成本地代码时,会查看IL代码引用了哪些类型。在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型。在AssemblyRef元数据表的记录项中,包含了构成程序集强名称的各个部分。JIT编译器获取所有这些部分:名称(无扩展名和路径)、版本、语言文化、公钥标记(如果被加载的程序集是弱命名的,那么标识中只包含:程序集的名称,将不再包含版本、语言文化、公钥标记信息),并把它们连接成一个字符串。然后CLR尝试将与该标识匹配的一个程序集加载到AppDomain中(如果还没加载的话)。在内部CLR通过System.Reflection.Assembly类的静态方法Load来尝试加载此程序集(可以显示的将一个程序集加载到AppDomain中)。

Assembly的Load方法
  • 加载强命名程序集

Load方法导致CLR向程序集应用一个版本绑定重定向策略,并在GAC(全局程序集缓存)中查找对应的程序集。如果没找到,就接着去应用程序的基目录、私有路径子目录和codebase位置查找。

  • 加载弱命名程序集

和加载强命名程序集不同的是,Load不会向应用程序应用版本绑定重定向策略,CLR也不会去GAC中查找程序集。

最后如果Load找到指定的程序集,就会返回已加载的程序集的一个Assembly对象的引用,如果没有加载到指定程序集,则会抛出一个System.IO.FileNotFoundException。

AppDomain的Load方法(尽量不使用)

与Assembly的Load方法不同,该方法属于实例方法。作用为将一个程序集加载到一个指定的AppDomain中。可供非托管代码调用,允许宿主将一个程序集注入一个指定的AppDomain。托管代码一般不使用该方法,因为在调用该方法后会开始在常规位置搜寻程序集,而AppDomain关联了一些设置告诉CLR如何查找程序集,为了加载程序集CLR将使用与指定AppDomain关联的设置,而不是发出调用的那个AppDomain关联的设置。以及AppDomain的Load方法返回对程序集的一个引用,由于System.Assembly类不是从System.MarshalByRefObject派生的,所以程序集必须按值封送回那个发起调用的AppDomain。现在CLR就会用发出调用的AppDomain的关联设置来定位并加载程序集。如果发出调用的AppDomain没有找到指定的程序集就会抛出FileNotFountException。这不是我们期望的行为,所以尽量避免使用AppDomain的Load方法。

Assembly的LoadFrom方法

在内部LoadFrom会先调用System.Reflection.AssemblyName类的静态方法GetAssemblyName,该方法打开指定的文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息,然后返回System.Reflection.AssemblyName对象并关闭文件。随后LoadFrom方法在内部调用Assembly的Load方法,将AssemblyName对象传给它。接着CLR会应用版本绑定重定向策略,并在各个位置查找程序集。如果Load找到了匹配的程序集就会加载并返回已加载程序集的一个Assembly对象,LoadFrom方法将返回这个值。如果没有找到匹配的程序集,LoadFrom就会加载通过LoadFrom的实参传递的路径中的程序集。如果已加载了一个具有相同标识的程序集,LoadFrom方法会简单返回代表已加载程序集的一个Assembly对象。

*可以给LoadFrom方法传递一个URL实参,CLR会主动下载文件并将其安装到用户的下载缓存中,最后再从那加载文件

Assembly的LoadFile方法

调用这个方法可以从任意路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDomain中。通过LoadFile加载程序集时,CLR不会自动解析任何依赖问题,代码必须向AppDomain的AssemblyResolve事件登记,让事件回调方法显示加载任何依赖的程序集。

Assembly的ReflectionOnlyLoadFrom方法和ReflectionOnlyLoad方法
  • ReflectionOnlyLoadFrom:加载由路径指定的文件,文件的强名称标识不会获取,也不会再GAC和其它位置搜索文件。
  • ReflectionOnlyLoad:会在GAC、应用程序基目录、私有路径和codebase指定的位置搜索指定的程序集。和Load不同的是不会应用版本控制策略,即指定的什么加载版本,获得的便是哪个版本。
  • ReflectionOnlyLoadFrom和ReflectionOnlyLoad:这两个方法加载程序集时,CLR都会禁止程序集中的任何代码执行。试图执行这两个方法加载的程序集中的代码,都会导致CLR抛出InvalidOperationException。
存在多个具有相同标识的程序集

一台机器上可能同事存在多个具有相同标识的程序集,由于LoadFrom会在内部调用Load方法,所以CLR有可能不加载你指定的文件,而是加载一个不同的文件,从而达不到预期效果。建议每次生成程序集都修改版本号,确保每个版本都有自己的唯一标识,进而确保LoadFrom方法能达到预期行为。

程序集卸载

CLR并未提供单独卸载某个程序集的功能,因为一旦这种行为被允许,那么一旦线程从某个方法返回至已卸载的程序集中的代码,应用程序就会崩溃。要卸载程序集必须卸载程序集所在的整个AppDomain。而使用ReflectionOnlyLoadFrom方法和ReflectionOnlyLoad方法加载的程序集由于程序集中的代码不允许被执行,让人看上去这些程序集是可以卸载的,实际上CLR也不允许卸载这两个方法加载的程序集,因为即便不会运行程序集中的代码但仍然可以利用反射来创建对象,引用程序集中定义的元数据,如果程序集被卸载就必须使这些对象无效,从实现复杂度还是执行速度上来讲跟踪这些对象的状态都是得不偿失的。

你可能感兴趣的:(程序员,c#,编程)