【问题标题】:How are references located in .NET?.NET 中的引用是如何定位的?
【发布时间】:2024-04-29 18:50:03
【问题描述】:

.NET 在运行时使用什么过程来定位被引用的程序集,它与在编译期间用于定位被引用程序集的过程有什么不同?我对搜索的位置及其搜索顺序以及可能影响结果的任何参数/配置设置特别感兴趣。

【问题讨论】:

  • 您能说得更具体些吗?您可以在以下位置看到引用的路径:解决方案 > 项目 > 引用 > “所需的引用” > 属性。
  • 问题改写为更具体。

标签: .net project-reference assembly-references


【解决方案1】:

在 .NET 应用程序中可以进行三种类型的引用。此答案仅涵盖以下列表中的前两个。

  1. 文件参考。
  2. 项目参考
  3. 服务参考。

必须解析每个引用。参考解析是以文件的形式定位参考的具体实例的过程。项目引用的解析方式与文件引用的解析方式相同。项目引用只允许您引用尚不存在的程序集(因为它是构建过程的输出。)

重要的是要理解引用解析在编译时和运行时都发生,并且两者的过程完全不同。不理解这一点会导致无休止的头痛。相信我,我知道。

运行时参考解析(又名绑定)

当一个应用程序被调用时,它必须被加载到内存中。如果应用程序使用另一个程序集中的对象,则该程序集也必须加载到内存中。 .NET 框架使用以下过程来执行此操作。

  1. 确定引用程序集的版本。
    • 在编译时将引用程序集的版本写入应用程序清单。除非在配置中被覆盖,否则将使用此版本。
      • 应用程序/web.config
      • 发布策略(覆盖 application/web.config)
      • machine.config(覆盖发布策略和 application/web.config)
  2. 如果程序集之前已加载,则从缓存中重新使用。
  3. 如果提供了强名称,请搜索 GAC。
  4. 探头
    • 如果指定了代码库元素,则使用。
      • 如果未找到则绑定失败。
      • 如果版本、文化或公钥不匹配,则绑定失败。
    • 搜索应用程序基路径。按简单名称匹配,如果第一个匹配是错误的版本,则失败。
      • 如果未提供文化,则搜索根目录,然后搜索根目录/[程序集名称]
      • 如果提供了文化,则搜索根/[文化],然后搜索根/[文化]/[程序集名称]。
      • 如果 web/app.config 指定探测元素,则在 privatePath 中搜索路径。路径必须相对于应用程序根目录。

欲了解更多信息,请参阅http://msdn.microsoft.com/en-us/library/yx7xezcf%28v=vs.110%29.aspx

编译时参考分辨率

编译时间解析在构建过程中发生在 MSBuild 中。 MSBuild 是 Visual Studio 和 TFS 使用的构建引擎。请注意,对于 ASP.NET 应用程序,当第一次访问动态组件(aspx、asc、asax、cshtml 等)时,会有一个额外的编译步骤。这两种情况的参考解决方案如下所述。

MSBuild

程序集解析发生在 ResolveAssemblyReferences MSBuild 目标中。此目标调用 ResolveAssemblyReference 任务,将 AssemblySearchPaths 的值传递给 SearchPaths 参数,该参数的赋值如下。

<PropertyGroup>
        <!--
        The SearchPaths property is set to find assemblies in the following order:

            (1) Files from current project - indicated by {CandidateAssemblyFiles}
            (2) $(ReferencePath) - the reference path property, which comes from the .USER file.
            (3) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.
            (4) The directory of MSBuild's "target" runtime from GetFrameworkPath.
                The "target" runtime folder is the folder of the runtime that MSBuild is a part of.
            (5) Registered assembly folders, indicated by {Registry:*,*,*}
            (6) Legacy registered assembly folders, indicated by {AssemblyFolders}
            (7) Resolve to the GAC.
            (8) Treat the reference's Include as if it were a real file name.
            (9) Look in the application's output folder (like bin\debug)
        -->
    <AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == ''">
      {CandidateAssemblyFiles};
      $(ReferencePath);
      {HintPathFromItem};
      {TargetFrameworkDirectory};
      {Registry:$(FrameworkRegistryBase),$(TargetFrameworkVersion),$(AssemblyFoldersSuffix)$(AssemblyFoldersExConditions)};
      {AssemblyFolders};
      {GAC};
      {RawFileName};
      $(OutDir)
    </AssemblySearchPaths>

这里发生了很多事情,我并不声称了解所有内容,但我会尝试指出重要的部分。

  1. 查找参考的最常见位置是(按搜索顺序)
    • 手动添加到项目的文件(例如 /lib/coollib.dll>
    • 提示路径指定的位置。
    • 广汽
    • 应用程序输出路径。
  2. 标记为 Copy Local = true 的引用在编译后复制到应用程序输出路径。这意味着此设置的值对 MSBuild 的引用解析过程没有影响。请注意,复制本地 UI 设置映射到项目文件中的 &lt;private&gt; 元素。
  3. MSBuild 将始终尝试使用可用于给定程序集的最新版本,除非指定特定版本 = true。此设置的默认值为 false,这意味着在搜索 GAC 时,将始终使用最新版本的 DLL,而不管项目定义中指定的版本。

ASP.NET 运行时编译器

除非之前在构建时使用预编译选项编译到项目输出文件夹中,否则所有动态内容(aspx、asc、asax、cshtml 等)都会在首次​​访问应用程序时在运行时编译一次。此动态内容还可以依赖于其他程序集。 system.web > 编译 > 程序集元素用于告诉 ASP.NET 运行时编译器这些依赖关系,以便它可以引用它们。

ASP.NET 运行时编译器将搜索以下位置以查找这些引用。

  1. 应用程序私有程序集缓存(又名 PAC),即 /bin 文件夹。
  2. GAC(如果使用强名称指定引用)。

请注意,默认情况下,根 web.config 使用通配符语法引用一些系统程序集和 PAC 中的所有程序集。这意味着您很少需要手动显式添加对 system.web > 编译 > 程序集元素的引用。在许多情况下,您可以并且应该完全删除该元素。它应该只包含对存储在 GAC 中的程序集的引用。使用 Copy Local = true 是包含 ASP.NET 运行时编译器所需的非 GAC 引用的推荐方法。

还请注意,如果您使用 system.web > 编译 > 程序集元素使用程序集的强名称指定特定版本号,则可能会出现许多细微错误。 ASP.NET 运行时编译器将尝试使用您指定的 exact 版本进行编译。如果在 MSBuild 编译阶段针对不同版本的程序集编译应用程序的非动态组件,这可能会导致问题。经常出现这种情况,因为 MSBuild 将使用它可以找到的最新版本,并且只有在您设置 specific version = true 时才会使用确切的版本。

其他资源:

http://jack.ukleja.com/diagnosing-asp-net-page-compilation-errors/ http://blog.fredrikhaglund.se/blog/2008/02/23/get-control-over-your-assembly-dependencies/ https://dhakshinamoorthy.wordpress.com/2011/10/01/msbuild-assembly-resolve-order/ http://www.beefycode.com/post/resolving-binary-references-in-msbuild.aspx

【讨论】:

  • 仅供参考,根据经验,仅仅因为在编译时引用了程序集,并且仅仅因为您的应用程序中的代码正在访问该程序集中的代码,并不意味着程序集是在运行时加载的。访问该程序集中代码的代码需要由应用程序调用。如果代码在运行时没有被调用,那么你需要使用 System.Reflection.Assembly.Load(“SomeAssembly”) 来加载程序集。我在这里写了一篇博客:medium.com/@DavidKlempfner/…