这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面。我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助。

本文范例代码可以通过这里下载 http://files.cnblogs.com/chenxizhang/AssemblyMatchDemoSolution.zip

根据程序集的特征,讨论这个问题,我们大致上有两个分类

没有做强名称签名的程序集

对于这种情况,CLR查找和加载程序集的方式如下

  1. 程序的根目录
  2. 根目录下面,与被引用程序集同名的子目录
  3. 根目录下面被明确定义为私有目录的子目录

同时,这种情况下,如果有定义codebase,则codebase的优先级最高,而且如果codebase指定的路径找不到,则直接报告错误,不再查找其他目录

有做强名称签名的程序集

对于这种情况,CLR查找和加载程序集的方式如下

  1. 全局程序集缓存
  2. 如果有定义codebase,则以codebase定义为准,如果codebase指定的路径找不到,则直接报告错误
  3. 程序的根目录
  4. 根目录下面,与被引用程序集同名的子目录
  5. 根目录下面被明确定义为私有目录的子目录

我们帮助大家更好地理解以上的说明,我准备用范例来做讲解。

1.准备基本范例

下面的范例演示了一个应用程序(MyApplication),和一个类库(MyLibrary) ,MyApplication是引用了MyLibrary的。

MyLibrary中有一个TestClass类型,提供了一个简单的方法(SayHello)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace MyLibrary
{
public class TestClass
{
public void SayHello()
{
//这里为了演示方便,显示出来当前加载的程序集完整路径
Console.WriteLine(this.GetType().Assembly.Location);
Console.WriteLine("Hello,world");
}
}
}

在MyApplication中,我们就是简单地创建了这个类型的实例,然后调用方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
var c = new MyLibrary.TestClass();
c.SayHello(); Console.Read();
}
}
}

默认情况下,如果我们编译整个项目,那么MyLibrary.dll会被自动地复制到MyApplication的根目录,如下图所示

运行MyApplication.exe,我们能看到下面这样的输出

我们可以很清楚地看到,当前加载的MyLibrary.dll是来自于MyApplication的根目录的。

2. 假如我们不想将MyLibrary.dll放在应用程序的根目录

有时候,我们会希望单独存放MyLibrary.dll,那么第一种做法就是,直接在应用程序根目录下面建立一个与程序集同名的子目录,然后将程序集放进去。

我们注意到,根目录下面的MyLibrary.dll 被移动到了MyLibrary目录

然后,我们再次运行MyApplication.exe,能看到下面这样的输出:

3.假如我们有很多程序集,希望统一放在一个目录

第二步的方法虽然不错,但有一个问题,就是如果我们引用的程序集很多的话,就需要在根目录下面建立很多子目录。那么,有没有办法统一地将这些程序集放在一个目录中呢?

我们可以通过如下的方式,定义一个特殊的私有路径(PrivatePath)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="libs"></probing>
</assemblyBinding>
</runtime>
</configuration>

同时,我们将MyLibrary.dll 移动到libs这个子目录下面去

然后,我们再次运行MyApplication.exe,能看到下面这样的输出:

这也就是说,对于没有签名的程序集,CLR一般会按照如下的规则查找和加载程序集

  1. 程序的根目录
  2. 根目录下面,与被引用程序集同名的子目录
  3. 根目录下面被明确定义为私有目录的子目录

但是,有一个例外

4. codebase的设置是优先的,而且是排他的

codebase是一个特殊的设置,我们可以在配置文件中明确地指定某个程序集的查找路径,这个规则具有最高的优先级,而且如果你做了设置,CLR就一定会按照你的设置去查找,如果找不到,它就报告失败,而不会继续查找其他路径。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="libs"/> <dependentAssembly>
<assemblyIdentity name="MyLibrary"
culture="neutral" />
<codeBase version="1.0.0.0"
href="CodeBase\MyLibrary.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

请注意,我们保留了libs目录和Mylibrary目录,而且根目录下面也保留了一个MyLibrary.dll。 实际上,当前我们一共有4个dll. 那么到底会加载哪一个呢?

这种情况下,如果codebase下面找不到MyLibrary.dll 会怎么样呢?

我们发现他是会报告错误的,而不会查找其他目录的程序集。

5.如果有强名称签名会怎么样呢?

对程序集进行强名称签名的好处是,可以将其添加到全局全局程序集缓存中。这样既可以实现程序集的共享,又可以从一定程度上提高性能。

签名后,我们将其添加到全局程序集缓存中去

那么这种情况下,不管我们在应用程序根目录(或者下面的子目录)有没有MyLibrary.dll ,CLR都是尝试先从全局程序集缓存中查找和加载的。

需要注意的是,如果程序集是经过了强名称签名,则在定义codebase的时候,应该注明publicKeyToken

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="libs"/> <dependentAssembly>
<assemblyIdentity name="MyLibrary"
publicKeyToken="4a77fca346941a6c"
culture="neutral" />
<codeBase version="1.0.0.0"
href="CodeBase\MyLibrary.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
 
 
 

总结

本文通过实例讲解了CLR在查找和加载程序集的时候所遵循的一些规则,针对有强名称和没有强名称的程序集,这些规则略有不同。本文范例代码可以通过这里下载 http://files.cnblogs.com/chenxizhang/AssemblyMatchDemoSolution.zip

相关问题

本文还没有涵盖到的另外两个特殊情况,在日常工作中不多见,大家有兴趣可以再找些资料研读。

1.在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe

2.如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录。

通常情况下,我们都就是程序集设置为语言中立的,所以不存在这个问题

【转】再谈CLR查找和加载程序集的方式的更多相关文章

  1. 再谈CLR查找和加载程序集的方式

    原文:再谈CLR查找和加载程序集的方式 这是一个老问题,以前也有朋友写过一些文章介绍,但可能还不是很全面.我也多次被人问到,这里结合案例再次谈谈,希望对大家有所帮助. 本文范例代码可以通过这里下载 h ...

  2. CLR查找和加载程序集的方式

    C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...

  3. CLR查找和加载程序集的方式(二) 流程图

    在前一篇文章<CLR查找和加载程序集的方式(一)>中详细介绍了CLR查找和加载程序的方式,分别介绍了配置与代码的实现方式. 本篇通过一个具体的流程图来帮助大家更加直观明了深入的掌握CLR查 ...

  4. CLR查找和加载程序集的方式(一)

    C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...

  5. CLR查找和加载程序集 z

    C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...

  6. linux系统——ld-linux.so.X查找和加载共享动态库的顺序

    ld-linux.so查找共享库的顺序: Glibc安装的库中有一个为ld-linux.so.X,其中X为一个数字,在不同的平台上名字也会不同.可以用ldd查看: #ldd /bin/cat linu ...

  7. CLR如何加载程序集以及程序集版本策略

    在项目的配置文件Web.config中,会看到<runtime>节点,以及包含在其中的<assemblyBinding>节点,这显然与程序集有关,这些节点到底何时被用到呢? 在 ...

  8. 五、CLR加载程序集代码时,JIT编译器对性能的产生的影响

    1.CLR首次加载代码造成的性能损失 四.CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,JITComplier函数(JIT编译器)会验证IL ...

  9. .Net Core 通过依赖注入和动态加载程序集实现宿程序和接口实现类库完全解构

    网上很多.Net Core依赖注入的例子代码,例如再宿主程序中要这样写: services.AddTransient<Interface1, Class1>(); 其中Interface1 ...

随机推荐

  1. Linux学习笔记 (四)归档和压缩

    一.zip压缩命令: 1.压缩文件: 格式:zip 压缩文件 源文件 例:zip abc.zip abc  //将abc文件压缩到abc.zip文件内. 2.压缩目录: 格式:zip –r 压缩目录 ...

  2. 怎样用Google APIs和Google的应用系统进行集成(4)----获得Access Token以通过一些Google APIs的OAuth2认证

    在上篇文章中: "怎样用Google APIs和Google的应用系统进行集成(3)----调用发现Google APIs的RESTful的服务"一文中,我们直接用jdk的java ...

  3. JavaScript逻辑运算符(操作数运算符)

    1.概述 ||(或)和&&(与)都是逻辑运算符.但是或/与叫“逻辑运算符”不太合适,叫“操作数运算符”更合适! 因为||(或)和&&(与)返回的不是布尔值,而是两个操作 ...

  4. DB2解锁

    1.登录数据库 db2 connect to 数据库名字 user 用户名 using 密码 2.进入db2top db2top -d 数据库名 进入到如下界面:  3.按下shift+u(图中U-L ...

  5. Spring HibernateTemplate

    HibernateTemplate利用模板设计模式,可将重复的opensession getcurrentsession工作省去,只将必要操作执行即可,其它的由spring来帮我们处理.   < ...

  6. Yii2.0 下的 load() 方法的使用

    一 问题 最近在使用 Yii2.0,遇到一个 bug:在 /models/OrderDetail.php add() 方法中调用 load() 方法加载数据,却加载不了. public functio ...

  7. Android or java https ssl exception

    1.http://www.trinea.cn/android/android-java-https-ssl-exception-2/ 2.http://www.eoeandroid.com/threa ...

  8. python机器学习-逻辑回归

    1.逻辑函数 假设数据集有n个独立的特征,x1到xn为样本的n个特征.常规的回归算法的目标是拟合出一个多项式函数,使得预测值与真实值的误差最小: 而我们希望这样的f(x)能够具有很好的逻辑判断性质,最 ...

  9. 【MyBatis学习01】宏观上把握MyBatis框架

    今天开始学习mybatis框架,博客主要记录学习过程中的一些总结,如有错误之处,欢迎留言指正~先用mybatis的鸟鸟来镇个楼,咳咳~~ mybatis框架是一个持久层框架,是Apache下的顶级项目 ...

  10. javascript 字符串对象新增 replaceAll 方法

    String.prototype.replaceAll = function(reallyDo, replaceWith, ignoreCase) { if(! RegExp.prototype.is ...