类型存在于两个程序集中。

101
我已经创建了两个.NET互操作组件,分别来自于两个不同的第三方COM DLL。这两个COM DLL都包含一个名称为COMMONTYPE的类型。因此,COMMONTYPE现在也通过这两个互操作组件公开。
我有一个需要使用这两个互操作组件的第三个项目,并且我遇到了臭名昭著的编译时错误:
引用中同时存在 <ABC> 类型于 <ASSEMBLY1.dll><ASSEMBLY2.dll> 由于COM DLL是由第三方供应商提供的,我无法访问源代码,并且我正在编写C#控制台应用程序,这意味着我没有web.config文件可以添加debug=false的解决方法。我该怎么办?

我来到这里是因为Visual Studio抱怨JsonConvert类型同时存在于Newtonsoft.Json.Torq和Newtonsoft.Json中。 - Matthew
6个回答

226
我知道这是老的,但有一种比列出的更简单的方法。当你引用两个具有相同名称和命名空间的程序集时,这种方法适用。
如果你右键点击对DLL的引用并选择属性,你会看到这里有一个叫做“别名”的属性。

enter image description here

默认值是"global"。对于其中一个冲突的程序集,请将其更改为任何其他值。在下面的示例中,我将其从"global"更改为"destination"。
接下来,在您的代码文件中,您将需要使用extern关键字将此别名用作这些类型的根级命名空间。在这个例子中,您需要将以下内容放在您的.cs文件的顶部:
extern alias destination

现在,在这个文件中,你可以引用两种类型。
extern alias destination;
namespace Test
{
    public static class TestClass
    {
        public static void Success()
        {
            var foo = destination::Some.Duplicate.Namespace.SomeDuplicateType();
            var bar = Some.Duplicate.Namespace.SomeDuplicateType();
        }
    }
}

或者,如果你只需要类中的一个类型,你也可以使用using来指定命名空间。
extern alias destination;
using destination::Some.Duplicate.Namespace;
namespace Test
{
    public static class TestClass
    {
        public static void Success()
        {
            var foo = SomeDuplicateType();
        }
    }
}

2
感谢更好的答案!这显然就是别名的基本用途。 - PandaWood
5
对我来说,这肯定是最好的答案。其他解决方案甚至不起作用,因为我有两个完全相同的dll文件,其中一个只是通过ILMerge合并到另一个文件中。问题解决了。 - Johnny
1
我想重新提出@bunkerdive的一个问题(最受欢迎的答案):如何在使用NuGet包时使其工作?如果您尝试更改别名,VS会给出错误:无法修改源自导入文件的已评估对象。 - Geordie
2
我进入了引用,选择了由Nuget安装的包,然后将别名设置为目标。非常顺利... - Zonus
1
@PedroGaspar 可能是因为你正在使用 Visual Studio "Community"。我使用的是 VS2017 Enterprise,而且我刚刚在 5 秒钟前实现了这个功能。一些较低版本的 VS 对单元测试的支持不太好。 - AlbatrossCafe
显示剩余6条评论

20

虽然这是一个老问题,但我发现有更简单的选择...... 选择您想要使用的引用...... 在属性下,将别名更改为“xyz” 现在在代码行顶部添加:

extern alias xyz;

然后使用以下方式添加:

using xyz.VENDOR2.Type;

或者另一种使用方式:

using OtherNameSpace = xyz.VENDOR2.Type;

现在你应该能够明确地按如下使用引用:

var abc = new xyz.VENDOR2.Type.abc();

或者
var abc = new OtherNameSpace.abc();

7
如果引用的是一个NUGET包而不是普通的dll,那么如何实现这一点呢? - bunkerdive
1
@bunkerdive,使用或不使用NuGet包,它的工作方式基本相同。当您安装NuGet包时,对包中程序集的引用应自动添加到您的Visual Studio项目中。在“解决方案资源管理器”视图窗格中,在项目下的“引用”节点下查看。 - Kenny Evitt
1
@bunkerdive,使用NuGet包和VS2017时,在属性下不会出现别名选项。因此,您必须按照答案在.csproj文件中添加手动别名。查找带有<Target>示例的答案。 - Wasted_Coder

14

除非供应商的名称空间相同(不太可能),否则在那一点上类型定义实际上将是分开的。你需要做的是(有时这真的很麻烦)在使用语句中创建一个命名空间别名,而不仅仅是随意应用该语句。这将允许你重新标识名称空间:

using Vendor1 = Vendor.Namespace;
using Vendor2 = OtherVendor.Namespace;

...

Vendor1.COMMONTYPE blah = new Vendor1.COMMONTYPE();
Vendor2.COMMONTYPE blah2 = new Vendor2.COMMONTYPE();

这将意味着为这些供应商中每个命名空间中定位的所有类型使用特定的别名。


这肯定会帮助我解决编译时错误。 但是,有没有什么方法可以让.NET在创建程序集时忽略特定类型,以便我只在一个程序集中拥有公共类型..? - Kou S Hal
1
@KouSHal:唯一的方法是构建一个包装类,引用供应商库中的一个类,并将所有类、扩展方法等作为代理公开。如果这是一个只有几个类的小型库,那么这不会太难,但如果像大多数供应商库一样,这将是一个噩梦般的创建和维护过程。 - Joel Etherton

7

我知道这已经比较老了,但最近我看到了这个。我发现修复这个问题的最佳方法是通过修改YourProjectName.csproj文件并添加以下内容:

<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
    <ItemGroup>
        <ReferencePath Condition="'%(FileName)' == 'Namespace.You.Want.To.Alias'">
            <Aliases>NewAliasForNamespace</Aliases>
        </ReferencePath>
    </ItemGroup>
</Target>

1

你可以使用别名来代表不同的命名空间和/或类型:

以下是示例:

using other = sssssss.a;
namespace ConsoleApplication1
{
    public class a 
    {
        public string ff { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            other s = new other();
            a b = new a();
        }
    }
}
namespace sssssss 
{

    public class a
    {
        public string ff { get; set; }
    }
}

MSDN


0
也许你可以通过改变一个程序集的命名空间来欺骗它,在这种情况下,一个 COMMONTYPE 的完全限定名称将不会等于另一个,可能它可以解决你在第三个 DLL 中出现的冲突问题。
希望这可以帮到你。

很遗憾,我不能这样做,因为我正在使用tlbimp从现有的COM dll生成.NET程序集。 - Kou S Hal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接