,,C#中应用程序集的装载过程详解

,,C#中应用程序集的装载过程详解

本文主要介绍C#中应用集加载过程的相关信息,通过示例代码进行了非常详细的介绍,对大家的学习或工作有一定的参考价值。有需要的朋友就跟着下面的边肖学习吧。

目录

了解C #中程序集如何加载程序集、模块和引用。网。程序集绑定绑定重定向当问题开始出现时,进行故障排除并记下参考

了解如何在C #中加载程序集。网

我们一直在处理库和NuGet包。无论好坏,先进。NET开发人员需要知道。NET运行时加载程序集。

这些库依赖于其他流行的库,并且有许多共享的依赖项。有了一个足够大的依赖网络,你就会陷入冲突或困境。处理此类问题的最佳方法是了解该机制的内部工作原理。

在本文中,您将看到。NET进程加载引用的程序集。

您将了解加载哪个库版本,当有多个可用版本时会发生什么,以及为什么有时会由于版本冲突而出现问题。

您将看到如何调试这些类型的问题,检查程序集绑定日志(fusion log)以及解决冲突的一些方法。

程序集、模块和引用

让我们从一些基本术语开始。NET进程。

中的程序集。NET是一个DLL或EXE文件。Visual Studio解决方案中的每个项目都被编译成一个程序集。

每个程序集可以包含多个模块,但事实上,我们几乎总是在一个程序集中有一个模块与程序集同名。

当您在Visual Studio中启动该过程或单击F5时,将执行启动项目程序集。除了。NET框架或者。NET核心程序集,它将是第一个加载的程序集。

之后,该流程将根据需要在运行时加载其他程序集。只有在需要调用其方法或使用其类型时,它才会延迟加载程序集。

下面是为一个简单的“Hello World”加载的模块。NET Framework项目(模块和程序集对于我们所有的意图和目的都是相同的)。MyStartup.dll是这里的开始项目:

时加载的模块。NET核心项目开始。

当从另一个项目引用一个项目时,被引用项目的DLL或EXE在构造时被复制到启动项目的Bin文件夹中。

通常是Bin \ Debug或Bin \ Release。在运行时,当您第一次在引用的项目中使用该类型时,CLR会在应用程序目录中查找与预期名称和版本相同的DLL文件。然后将程序集加载到流程中。这也称为绑定到程序集。

这是一个例子:

假设我们有一个名为MyStartup的简单控制台应用程序,它引用了另一个名为Lib1的项目。MyStartup使用了Lib1程序集中的一些类。

在我的Startup中:

班级计划

{

静态void Main(string[] args)

{

int a=int。解析(控制台。ReadLine());

int b=int。解析(控制台。ReadLine());

控制台。WriteLine('A B=' Add(a,B));

}

私有静态int Add(int a,int b)

{

var计算器=新Lib1。计算器();

退货计算器。Sum(a,b);

}

}

在Lib1中:

公共类计算器

{

公共int Sum(int a,int b)

{

返回a b;

}

}

当进入Main方法时,Lib1程序集尚未加载。但是,当进入Add方法时,CLR会尝试解析Calculator类型,找出它在引用的程序集Lib1中,然后尝试加载该程序集。

程序集绑定在。网

当CLR需要加载一个程序集时,逻辑实际上比在Bin文件夹中搜索要复杂一点。这是实际的执行逻辑(有关详细信息,请参见Microsoft文档[1]):

1.根据配置文件(app.config或web.config)确定要加载的程序集的版本。配置文件的名字是(生成后)【可执行名】. exe.config或者web.config重定向在这里起作用(后面会详细介绍)。

2.检查程序集是否已加载。如果加载了其他版本,将会引发FileLoadException,除非它是可以同时加载多个版本的强名称程序集。

3.如果是强名称程序集,请检查全局程序集缓存[2](GAC)。GAC是在机器上共享多个应用程序部件的地方。如有必要,将缓存该程序集。它只能存储具有强名称的程序集。它可以存储同一程序集的不同版本。可以用gacutil.exe[3]自己安装到GAC。

4.如果它是一个具有强名称的程序集,并且配置文件包含一个codeBase节点,那么它将检查程序集在那里的位置。如果codeBase节点存在,但找不到程序集,FileNotFoundException将引发。

5.根据启发式算法检查程序集DLL或EXE。这个过程被称为“探测”。算法如下:

1.检查文件夹[应用程序库]/[程序集名称]。dll。应用程序库是应用程序可执行文件所在的位置。通常是Bin \ Debug或Bin \ Release文件夹。

2.检查[应用程序库]/[程序集名称]/[程序集名称]。动态链接库

3.如果为引用的程序集指定了区域性信息,则只检查以下目录:[application base]/[culture]/[assembly name]。dll[应用程序库]/[区域性]/[程序集名称]。动态链接库

4.如果探测节点存在于配置文件中,它将在privatePath节点的属性指定的文件夹中查找程序集。

为什么他们让一切变得如此困难,对吗?

其实这种逻辑对我们的发展很有帮助,不会把事情搞复杂。它的存在是为了实现一些重要的目标:

为了确保您引用的是特定的程序集和版本,将加载确切的版本。否则,将引发异常。此外,如果您知道自己在做什么,您可以在配置文件中指定覆盖规则(绑定重定向)。

以便在要加载的程序集中保持灵活性。例如,如果您想根据不同的区域性(语言)加载不同的程序集,您可以很容易地做到这一点。或者,如果您想根据客户配置加载不同的程序集,那也没问题。

为了安全起见,我们使用全名程序集。确保不能“伪造”程序集。例如,如果一个进程想要加载Lib1 v4.5,那么您将无法加载具有相同名称和版本的恶意软件程序集。加载时引发异常。这就是计算机上所有进程共享的GAC只接受强名称程序集的原因。

在大多数应用程序中,您不需要记住程序集加载和探测的复杂逻辑。你不需要知道或考虑GAC,全称程序集或操作配置文件。

你几乎完全不需要考虑库的版本,因为可能的冲突都是通过一种叫做“绑定重定向”的机制自动解决的。

绑定重定向

如果有一件事对理解这笔交易非常重要,那就是绑定重定向。能够告诉运行时它将实际加载哪个版本,而不管它引用的是哪个版本。

这是一个例子:你的过程有两个项目(模块):项目A和项目B,项目A引用log4net.dll的v1.1,项目B引用log4net.dll的v1.2。两个log4net DLL文件都被复制到输出文件夹,但只能有一个log4net.dll文件。

假设复制到输出文件夹的文件是log4net.dll v 1.2 .假设到达的第一个代码是项目A中的代码,引用log4net v1.1运行时会在输出文件夹中查找,找到log4net的不同版本,并失败FileLoadException。

还有一种可能。假设先执行项目B中的代码,在尝试使用log4net时,成功加载了log4net.dll v 1.2,过一会儿项目A中的代码会尝试使用log4net v1.1请看到程序集已经加载了其他版本并抛出了FileLoadException

如果您知道哪个log4net版本将出现在输出文件夹中,那么在这种情况下,您可以告诉运行时应该使用哪个版本。只需app.config将以下几行添加到此运行时部分的文件中:

?xml版本='1.0 '编码='utf-8 '?

配置

.

运行时间

.

assembly binding xmlns=' urn:schemas-Microsoft-com:ASM . v1 '

从属程序集

assemblyIdentity name='log4net '

public key token=' 669 E0 ddf 0 bb 1 aa 2 a ' culture=' neutral '/

bindingRedirect旧版本='0.0.0.0-5.0.0.0 '新版本='1.2.0' /

/dependentAssembly

/assemblyBinding

/运行时

.

/配置

这意味着每当运行时希望绑定到版本范围为0 . 0 . 0 . 0 . 0到的程序集log4net 5.0.0.0时,它将尝试绑定到版本1.2.0。

实际上,您不必手动添加这些重定向,因为它们是自动添加的。如果转到启动项目的属性,您将看到以下设置:

默认情况下,此选项处于选中状态。它自动检测版本冲突,并在。配置文件。

当问题开始出现时

乍一看,绑定重定向似乎可以解决所有问题,但事实并非如此。当使用绑定重定向时,使用的库版本基本上不同于预期的版本。方法被删除了怎么办?或者方法的签名改变了?在这种情况下,当调用此方法时,程序将由于运行时错误而失败。毕竟,版本的产生是有原因的。

如果这些问题确实存在,也有解决办法。查看我的文章:如何解决?NET引用和NuGet包[4]。

排除故障

当你有一个FileLoadException或者类似的东西时,我推荐你做的第一件事就是看看Visual Studio中的“模块”窗口。在这里,您将看到所有已加载的模块,并确定要加载的程序集是否已加载、使用哪个版本以及从哪个路径加载。

此外,您还可以查看程序集绑定日志,也称为融合日志。这些日志将确切显示在程序集绑定尝试期间发生了什么。您将看到运行时找到的程序集版本、运行时找到的文件夹以及故障点。

有几种方法可以查看融合日志。首先,您必须启用它们,因为它们在默认情况下是禁用的。您可以在注册表中手动启用它们,方法是将HKLM \软件\ Microsoft \ fusion \ forcelog值设置为1,将HKLM \软件\ Microsoft \ fusion \ logpath值设置为。日志将自动出现。或者,您可以使用Fusion Log Viewer,它应该安装在fuslogvw.exe的PC上。我建议使用“所有窗口”搜索的[5]类程序来查找。确保使用管理员权限运行Fusion Log Viewer,以便能够启用和禁用日志。最近一个更流行、更现代的工具是Fusion [6]。

旁注

也许你不知道,但是我以前讨厌处理这样的问题。比如一个逻辑问题,让我构建一个东西,甚至解决一个制作错误,但其他问题都好说,只有这一个。

这件事我没办法,只好苦学汇编绑定的内功。我发现,就像其他事情一样,一旦你理解了某件事,它就变得不那么可怕,甚至不那么有趣了。

所以,希望这篇文章对你有意义,在我的路上能给你提供快速的帮助。

参考

[1]微软文档:https://docs . Microsoft . com/en-us/dot net/framework/deployment/how-the-runtime-locations-assemblies

[2]全局程序集缓存:https://docs . Microsoft . com/en-us/dot net/framework/app-domains/GAC

[3]gacutil . exe:https://docs . Microsoft . com/en-us/dot net/framework/tools/gacutil-exe-GAC-tool

[4]如何解决版本之间的冲突。NET引用和NuGet包:https://michaelscodingspot . com/how-to-resolve-NET-reference-and-NuGet-package-version-conflicts/

[5]所有窗口”搜索:https://www.voidtools.com/

[6]融合:https://github.com/awaescher/Fusion/

关于C#应用集加载过程的详细讲解,本文到此为止。有关C#应用程序集加载过程的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • 设计一个简单的C#控制台应用程序,C#控制台程序,C# 创建控制台应用程序
  • 深入解析windows第8版,深入解析C#(第4版)
  • 数组代码,c# 数组操作,C# 数组实例介绍(图文)
  • 学会C#要多久,学会c#要多久,c#学习之30分钟学会XAML
  • 回溯法01背包问题c,回溯法求解01背包问题伪代码,C#使用回溯法解决背包问题实例分析
  • xml文件转义字符,xml转意字符,C# XML中的转义字符操作
  • winform 进度条控件,c# 进度条使用
  • winform 进度条控件,c# 进度条使用,C#使用winform实现进度条效果
  • winform backgroundworker,c# isbackground
  • winform backgroundworker,c# isbackground,C# BackgroundWorker用法详解
  • lua与c#交互,lua c#
  • lua与c#交互,lua c#,ToLua框架下C#与Lua代码的互调操作
  • linq c#,linq原理 c#
  • linq c#,linq原理 c#,c#中LINQ的基本用法实例
  • java decimal保留两位小数,sql中decimal函数保留2位小数,C#中decimal保留2位有效小数的实现方法
  • 留言与评论(共有 条评论)
       
    验证码: