我可以让我的程序集引用其他程序集的任何版本吗

本文关键字:程序集 任何 版本 引用 我的 我可以 其他 | 更新日期: 2023-09-27 18:06:40

  • 我正在开发一个类库(MyClassLibrary(
  • 我依赖第三方类库(ThirdPartyClassLibrary(
  • 我需要使用与我的用户相同版本的ThirdPartyClassLibrary。例如,如果我在ThirdPartyClassLibrary中设置了一个静态值,则用户需要看到该变化
  • 我班的用户可能依赖于ThirdPartyClassLibrary的4个不同版本中的任何一个
  • ThirdPartyClassLibrary很大,我不想把它和我的软件一起分发
  • 我已经反思了ThirdPartyClassLibrary的所有4个版本,并验证了我将使用它们做的事情在所有版本中都是兼容的(接口相同,方法签名相同,等等(
  • 我需要调用ThirdPartyClassLibrary才能发挥作用!每次我需要打电话的时候,我都无法反思每一件事
  • MyClassLibrary将在运行时加载,所以我不能指望用户会干扰程序集绑定重定向或其他开发时设置(或者任何设置,我的用户都不愿意做任何事情(
  • 我希望从代码的编译时检查中受益,所以理想情况下根本不进行反射

我如何编写MyClassLibrary,以便在将其加载到进程中时,无论用户加载了哪个版本的ThirdPartyClassLibrary,一切都能正常工作?

我可以让我的程序集引用其他程序集的任何版本吗

一种解决方法是在运行时使用AppDomain.AssemblyResolve事件。每当程序集的解析失败时,就会触发此项。您可以使用它将程序集的不同版本加载到CLR试图加载的程序集。

我在GitHub上添加了一个非常简单的演示:

https://github.com/danmalcolm/AssemblyResolutionDemo

设置如下:

  • 主应用程序App.exe直接引用程序集ThirdPartyLibrary.dll 2.0.0.0版本。

  • 它还引用了MyLibrary,它引用了旧版本的ThirdPartyLibrary 1.0.0.0。

  • AppDomain.AssemblyResolve事件用于在1.0.0.0版本加载失败时重定向到应用程序使用的版本

AssemblyResolve的处理方式如下:

public static void Initialise()
{
    AppDomain.CurrentDomain.AssemblyResolve += ResolveThirdPartyLibrary;
}
private static Assembly ResolveThirdPartyLibrary(object sender, ResolveEventArgs args)
{
    // Check that CLR is loading the version of ThirdPartyLibrary referenced by MyLibrary
    if (args.Name.Equals("ThirdPartyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fbcbfac3e44fefed"))
    {
        try
        {
            // Load from application's base directory. Alternative logic might be needed if you need to 
            // load from GAC etc. However, note that calling certain overloads of Assembly.Load will result
            // in the AssemblyResolve event from firing recursively - see recommendations in
            // http://msdn.microsoft.com/en-us/library/ff527268.aspx for further info
            var assembly = Assembly.LoadFrom("ThirdPartyLibrary.dll");
            return assembly;
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
    return null;
}

在加载ThirdPartyLibrary之前,我们需要绑定到事件,因此需要显式Initialize方法。

还要注意,只有当程序集的解析失败时,才会触发该事件。如果MyClassLibrary(1.0.0.0(引用的ThirdPartyLibrary版本在GAC中可用,则它将被成功加载,AssemblyResolve不会激发。然后会有两个不同的版本在使用。

我在这里演示了这个机制可以,我并不是说这是个好主意。根据应用程序运行的环境以及如何设置/安装/维护等,您需要考虑以下几点。

不,您不能以"只使用运行时可用的ThirdPartyClassLibrary.dll的任何版本"的方式,通过引用ThirdPartyClassLibrary来构建MyClassLibrary。

生成库时,任何引用的程序集的版本号都包含在程序集清单中。对您的程序集运行ILDASM工具将显示如下内容:

...
.assembly extern ThirdPartyClassLibrary
{
  ...
  .ver 1:0:0:0
}
...

ThirdPartyClassLibrary的名称和版本都已指定。在运行时,CLR将在首次运行MyClassLibrary.dll中引用它的指令时尝试加载ThirdPartyClassLibrary.dll。它将专门查找版本1.0.0.0的ThirdPartyClassLibrarydll(如果它是强命名程序集,还需要匹配的公钥(。

以下是CLR如何在运行时定位和绑定到程序集的快速概述(详细信息请访问http://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110(.aspx(:

步骤1-通过检查配置文件确定正确的程序集版本-我们将在下面返回,但目前,如果您不告诉它其他情况,CLR将尝试加载引用程序集中指定的确切版本,因此它将寻找1.0.0.0版本。

步骤2-检查程序集名称以前是否已绑定到,如果已绑定,则使用以前加载的程序集。请注意,此上下文中的"程序集名称"包括名称版本、公钥令牌等,而不仅仅是dll文件的名称。

步骤3-检查全局程序集缓存GAC(仅限强命名程序集(

步骤4-通过代码库或探测来定位程序集-本质上CLR会在不同的位置查找AssemblyB.dll(特定版本(。如果找不到特定的版本,就会发生错误。它不会自动恢复到早期或更高版本。

不幸的是,这意味着事情不会"只起作用"并支持你上面所描述的。如果引用MyClassLibrary的应用程序本身引用ThirdPartyClassLibrary的更高版本(2.0.0.0(,则在解析MyClassLibrary对ThirdPartyClass Library的引用时可能会发生一些不好的事情:

  • CLR找不到程序集A使用的程序集B的1.0.0.0版本,因此发生错误
  • 1.0.0.0版本恰好安装在GAC中并成功加载。当应用程序中的代码使用ThirdPartyClassLibrary 2.0.0.0版本时,您的库使用ThirdPartyClassLibrary 1.0.0.0版本

您可以做的一件事是使用库配置应用程序,以便CLR将对ThirdPartyClassLibrary.dll不同版本的引用统一为一个版本。这让我们回到上面概述的程序集绑定过程的步骤1——我们本质上改变了CLR正在寻找的ThirdPartyClassLibrary的版本。

绑定重定向(http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx)设计用于将对程序集不同版本的引用传递到单个版本。这些通常在应用程序的配置文件(Web.config、MyApp.exe.config(中定义,但也可以在计算机级别(machine.config(全局定义

以下是绑定重定向的示例,它将ThirdPartyClassLibrary.dll的所有早期版本重定向到2.0.0.0:

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="AssemblyB" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

请注意,这可以由Visual Studio 2013自动处理,它检测引用程序集的不同版本的情况,并为您添加绑定重定向。NuGet包管理器控制台中还有一个Add-BindingRedirect命令。

绑定重定向是一种可能的解决方案,在某些情况下可能是一种务实的选择。然而,它们也可能会混淆您库的用户。如果可行,您应该考虑分发针对不同版本的第三方库构建的不同版本的库。