了解如何在 C# .NET 中加载程序集
我们一直在处理库和 NuGet 包。无论好坏vs2015用c# 控制台程序,高级 .NET 开发人员都需要了解 .NET 运行时如何加载程序集。
这些库依赖于其他流行的库,并且有许多共享依赖项。拥有足够大的依赖网络,您最终会遇到冲突或困境。处理此类问题的最佳方法是了解该机制在内部是如何工作的。
在本文中,您将了解 .NET 进程如何以及何时加载引用的程序集。
您将了解加载了哪个库版本,当有多个版本可用时会发生什么,以及为什么有时会由于版本冲突而出现问题。
您将看到如何调试这些类型的问题、查看程序集绑定日志(融合日志)以及解决冲突的一些方法。
程序集、模块和参考
让我们从围绕 .NET 进程的一些基本术语开始。
.NET 中的程序集是 DLL 或 EXE 文件。Visual Studio 解决方案中的每个项目都被编译成一个程序集。
每个程序集可以包含多个模块,但实际上我们几乎总是在程序集中有一个与程序集同名的模块。
当您在 Visual Studio 中启动进程或按 F5 时,将执行启动项目程序集。它将是第一个加载的程序集,而不是 .NET Framework 或 .NET Core 程序集。
之后,该进程将根据需要在运行时加载其他程序集。它仅在需要调用程序集的方法或使用程序集的类型时才延迟加载程序集。
这是为一个简单的“Hello World”.NET Framework 项目加载的模块(对于我们的所有意图和目的,模块和程序集都是相同的)。MyStartup.dll 是这里的启动项目:
.NET Core 项目启动时加载的模块
当您从另一个项目中引用一个项目时,在构建时,所引用项目的 DLL 或 EXE 将被复制到启动项目的 Bin 文件夹中。
通常是 BinDebug 或 BinRelease。在运行时,当您第一次在引用的项目中使用类型时,CLR 会在应用程序目录中查找与预期名称和版本相同的 DLL 文件。然后将程序集加载到进程中。这也称为绑定到程序集。
这是一个例子:
假设我们有一个名为 MyStartup 的简单控制台应用程序,它引用了另一个名为 Lib1 的项目。MyStartup 使用 Lib1 程序集中的一些类。
在我的启动中:
在 Lib1 中:
进入方法时,Lib1 程序集尚未加载。但是,当输入一个方法时,CLR 会尝试解析类型,发现它在引用的程序集 Lib1 中,然后尝试加载该程序集。
.NET 中的程序集绑定
当 CLR 需要加载程序集时,逻辑实际上比在 Bin 文件夹中查找要复杂一些。这是执行的实际逻辑(有关详细说明,请参阅 Microsoft 文档 [1]):
1.根据配置文件(app.config或web.config)确定需要加载的程序集版本。配置文件的名称是(生成后)或 . 绑定重定向在这里发挥作用(稍后会详细介绍)。2.查看是否加载了程序集。如果加载了其他版本,则会抛出 FileLoadException,除非它是一个可以同时加载多个版本的强命名程序集。3.如果是强名称程序集,请检查全局程序集缓存 [2] (GAC)。GAC 是在一台机器上共享多个应用程序部分的地方。如果需要,程序集会被缓存。它只能存储强命名的程序集。它可以存储同一程序集的不同版本。您可以使用 gacutil.exe[3] 自行将其安装到 GAC。4.如果是强名称程序集,并且配置文件包含节点,然后它检查那里的装配位置。如果节点存在并且无法找到程序集,则会引发 A。5.根据启发式检查程序集 DLL 或 EXE。这个过程称为“探测”。算法如下:1.检查文件夹。应用程序库是应用程序可执行文件所在的位置。通常,您的 BinDebug 或 BinRelease 文件夹。2.检查3.如果为引用的程序集指定了区域性信息,则只检查以下目录:4.如果节点存在于配置文件中,则会在文件夹中查找程序集由属性指定。@5.根据启发式检查程序集 DLL 或 EXE。这个过程称为“探测”。算法如下:1.检查文件夹。应用程序库是应用程序可执行文件所在的位置。通常,您的 BinDebug 或 BinRelease 文件夹。2.检查3.如果为引用的程序集指定了区域性信息,则只检查以下目录:4.如果节点存在于配置文件中,则会在文件夹中查找程序集由属性指定。@5.根据启发式检查程序集 DLL 或 EXE。这个过程称为“探测”。算法如下:1.检查文件夹。应用程序库是应用程序可执行文件所在的位置。通常,您的 BinDebug 或 BinRelease 文件夹。2.检查3.如果为引用的程序集指定了区域性信息,则只检查以下目录:4.如果节点存在于配置文件中,则会在文件夹中查找程序集由属性指定。
为什么他们让一切变得如此困难,对吧?
其实这个逻辑对我们的开发是很有帮助的,而且不至于为难。它的存在是为了实现一些重要目标:
为确保您引用的是特定程序集和版本,将加载该确切版本。否则会抛出异常。而且,如果您知道自己在做什么,则可以在配置文件中指定覆盖规则(绑定重定向)。为了灵活地加载您想要加载的程序集。例如,如果您想根据不同的文化(语言)加载不同的程序集,您可以轻松地做到这一点。或者,如果您想根据客户配置加载不同的程序集,也可以。为了安全起见,我们使用程序集的全名。他们确保您不能“伪造”组件。例如,如果一个进程希望加载 Lib1 v4.5,您将无法加载具有相同名称和版本的恶意软件程序集。加载时抛出异常。这就是为什么 GAC,
在大多数应用程序中,您不需要记住程序集加载和探测的复杂逻辑。您无需了解或考虑 GAC、全名程序集或操作配置文件。
您几乎不需要考虑库的版本vs2015用c# 控制台程序,因为可能的冲突会通过一种称为“绑定重定向”的机制自动解决。
绑定重定向
如果要理解这笔交易有一件很重要的事情,那就是绑定重定向。能够告诉运行时它将实际加载哪个版本,而不管它引用的版本是什么。
下面是一个示例:您的流程有两个项目(模块):项目 A 和项目 B。项目 A 引用 log4net.dll v1.1,项目 B 引用 log4net.dll v1.2。两个 log4net DLL 文件都复制到输出文件夹,但只能有一个 log4net.dll 文件。
假设复制到输出文件夹的文件是 log4net.dllv1.2。假设第一个到达的代码是项目 A 中引用 log4net v1.1 的代码。运行时将查看输出文件夹,找到不同版本的 log4net,然后失败。
还有另一种可能。假设先执行项目B中的代码,尝试使用log4net时,成功加载log4net.dll v1.2。片刻之后,项目 A 中的代码将尝试使用 log4net v1.1,看到程序集已经加载了不同的版本,然后抛出。
如果您知道哪个 log4net 版本将在输出文件夹中,那么在这种情况下您所能做的就是告诉运行时它应该使用哪个版本。只需将以下行添加到该部分的文件中:
这意味着每当运行时想要绑定到版本范围为 log4net 的程序集时,它都会尝试绑定到版本。
实际上,您不必手动添加这些重定向,因为它们是自动添加的。如果您转到启动项目的“属性”,您将看到以下设置:
ͼƬ-20200711101325128
默认选择此选项。它会自动检测版本冲突并在文件中生成绑定重定向。
当问题开始出现时
乍一看,绑定重定向似乎是所有问题的答案,但事实并非如此。使用绑定重定向时,基本上使用了与预期不同的库版本。如果方法被删除怎么办?还是方法的签名发生了变化?在这种情况下,调用该方法时程序将失败并出现运行时错误。毕竟,创建版本是有原因的。
如果确实存在此类问题,则有一种解决方法。查看我的文章:如何解决 .NET 引用和 NuGet 包版本冲突 [4]。
故障排除
当你有一个或类似的东西时,我建议做的第一件事是查看 Visual Studio 中的“模块”窗口。在这里,您将看到所有已加载的模块,并确定您尝试加载的程序集是否已加载、使用哪个版本以及从哪个路径加载。
除此之外,您还可以查看程序集绑定日志,也称为融合日志。这些日志将显示在程序集绑定尝试期间究竟发生了什么。您将看到运行时查找的程序集版本、运行时查找的文件夹以及故障点。
有几种方法可以查看融合日志。首先,您必须启用它们,因为默认情况下它们是禁用的。您可以通过将值设置为 1 并将值设置为 来在注册表中手动启用它们。将自动出现一个日志。或者,您可以使用 Fusion Log Viewer,它应该以这种方式安装在 PC 上。我建议使用“Everything Window”搜索 [5] 之类的程序来找到它。确保以管理员权限运行 Fusion Log Viewer,以便能够启用和禁用日志。最近变得更流行的更现代的工具是 Fusion++ [6]。
边注
也许你不知道,但我曾经讨厌不得不处理这种事情。例如一个逻辑问题,让我构建一些东西,甚至修复一个生产错误,但其他一切都很好,但是这个……。
在这件事上别无选择,我不得不硬着头皮学习汇编绑定的内部工作原理。我发现,就像其他所有事情一样,一旦你理解了某件事,它就会变得不那么可怕,甚至不那么有趣。
所以我希望这篇文章能引起你的兴趣,并在我走过的路上给你快速的帮助。
参考
微软文档:
全局程序集缓存:
gacutil.exe:
如何解决 .NET 引用和 NuGet 包版本冲突:
所有窗口”搜索:
融合++:
技术群:如需加入技术群学习交流,请加小编微信,记得备注:加群,如对以上内容有任何疑问,可直接与小编交流!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 欧资源网