问题 使用两个具有相同名称和相同名称空间的DLL


我有一个项目需要引用两个具有相同名称的DLL。 DLL名称不强,具有相同的名称。

我需要访问每个DLL中的某些类型,但这些类型具有相同的完全限定名称。 所以我们假设第一个是companyDLL.dll someProduct.Type1 和 第二个是companyDLL.dll with someProduct.Type1

我怎样才能访问这两个 Type1 同一个项目中的课程?

我已经尝试过使用了 extern alias,但它需要我更改其中一个DLL的名称。


4007
2017-12-14 23:05


起源

这完全属于“如果你试图这样做,你的设计严重受损”。 - cdhowie
绝对同意,这是一个需要两个版本的公司产品(使用.NET构建)的信息的工具。但是这里讨论的产品受到监管,因此无法改变。 - varosh
@cdhowie不一定。有许多不同的场景涉及旧版本和新版本之间的转换,或者例如基准比较。 - BartoszKP


答案:


如果您的两个DLL具有相同的名称,则必须重命名它们。例如Assembly1.dll和Assembly2.dll。

像往常一样在项目中添加这些DLL作为引用,并在每个引用的属性中指定别名。

在使用DLL时使用的代码中 extern alias 指定要引用的dll。

extern alias Assembly1Reference;
using Assembly1Reference::AssemblyNamespace.MyClass;

如果你这样离开,你很可能会得到一个 FileNotFoundException 说它无法加载文件或程序集。要解决此问题,您需要添加一个 ResolveEventHandler 这将加载您尝试使用的正确程序集。为此,您必须准确指定存储DLL文件的位置。在下面的例子中,我手动将Dll文件复制到项目调试文件夹。如果它显示“assembly1的名称”,您可以在引用DLL,构建项目并使用记事本打开csproj文件后找到该名称。要查找的内容将在我的示例代码下面。

extern alias Assembly1Reference;
extern alias Assembly2Reference;

static void Load()
{
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    Do();
}

static void Do()
{
    new Assembly1Reference.Assembly.Class();
    new Assembly2Reference.Assembly.Class();
}

static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string currentPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    if(args.Name == "Name of assembly1")//Found in csproj file after referenced and built
    {
        return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(currentPath, "Assembly1.dll"));
    }
    if(args.Name == "Name of assembly2")//Found in csproj file after referenced and built
    {
        return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(currentPath, "Assembly2.dll"));
    }
    return null;
}

正如所承诺的,这是csproj文件中的引用。名称是include属性中的所有内容。

<Reference Include="MyAssembly_3.6.2.0, Version=3.6.2.0, Culture=neutral, PublicKeyToken=12341234asdafs43, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>Resources\Assembly1.dll</HintPath>
      <Aliases>Assembly1Reference</Aliases>
</Reference>

我知道这已经很晚了,但希望它能帮助任何人从现在开始访问这个页面。


10
2018-03-18 17:00



这很有用,我可以部分地完成这项工作,但我有以下问题。这样做的目的是什么? static void Do() { new Assembly1Reference.Assembly.Class(); new Assembly2Reference.Assembly.Class(); } 通过代码而不是通过app.config来解析程序集的优点是什么? 这个。就我而言,总是会加载最新版本的程序集。我该怎么做才能加载特定重命名程序集的精确dll。 - Rajaraman


运用 extern alias 将程序集带入不同的名称空间。如果您可以区分命名空间,那么您应该可以使用 using altType1 = someProduct.Type1 为该类型创建一个本地别名。

首先从命令行限定程序集:

/r:ProductA=companyDLLA.dll
/r:ProductB=companyDLLB.dll

然后使用引用它们 extern alias

extern alias productA;
extern alias productB;

最后,您可以为本地类型添加别名:

using productTypeA = productA.Type1;
using productTypeB = productB.Type1;

3
2017-12-14 23:16



不幸的是,这两个dll有相同的名字.i.e。/ r:ProductA=companyDLLA.dll /r:ProductB=companyDLLA.dll - varosh
有没有理由你不能重命名DLL?如果我在你的情况下,我会尝试重命名它们 companyDLLv1.dll 和companyDLLv2.dll`。 - Greg Buehler
是的,无法重命名DLL。这里讨论的DLL受到监管,如果不通过官方批准程序就无法更改(这对我的方案来说是不可能的) - varosh


我可以想到两种方法来处理这个问题。

  1. 将每个DLL加载到单独的AppDomain中。您必须跨AppDomain边界进行调用以触发各种属性和方法。

  2. 在加载程序集之前,反汇编然后重新组装每个程序集并将它们放入唯一(可能是动态生成的)名称空间。有一些工具可以帮助解决这个问题(1)(2)。你可以自动化它。

但我的基本感觉是,你真的不应该这样做。在上游解决这个问题比在已经编译好的程序集之后解决它更容易。


0
2017-12-14 23:15



Cecil可能是比ildasm更好的方法,因为您可以使用C#直接操作IL。 - cdhowie
对于上面的#1:如何在同一个项目中添加对同名的两个DLL的引用? - varosh


所提出的解决方案都不适合我。我不得不重新编译DLL并给它们不同的标识,即在项目设置中更改“程序集名称”。

如果您无法重新编译DLL,另一种解决方案是创建具有合适名称的包装器。


0
2017-08-29 11:28