猫头鹰和全球问题:连接具有相同名称空间和类名称的两个程序集


今晚, gelas开始了有关程序包管理器如何在不同平台上工作的对话。 在对话过程中,我们讨论了一种情况,当您需要将两个库连接到.NET Core项目时,这些库包含相同名称空间中的相同名称的类。 由于我做了很多.NET Core,因此我想检查一下如何解决此问题。 进一步说明


免责声明 。 这种情况经常发生吗? 使用.NET十多年来,我从未在实际项目中处理过类似情况。 但是实验很有趣。


为了以防万一,我将澄清我将使用以下方法进行实验:


  • macOS 10.13,
  • .NET Core SDK 2.1.302
  • 骑士2018.2

因此,我们将模拟一个情况,即当我们得到两个具有必须在项目中使用的类的库时。 同时,我们无权访问源代码,但是我们无法反汇编这些程序集以更改它们中的名称空间,因此我们也无法进行反编译。


实验准备


因此,对于初学者,请准备一头猫头鹰和两个地球仪。 猫头鹰,我们将有一个针对netcoreapp2.1的项目。 我们将创建两个作为Globe的项目,其中一个也将针对netcoreapp2.1,第二个将针对netstandard2.0



在每个项目中,我们放置Globe类,它们将在相同的名称空间中,但是它们将具有不同的实现:


第一个档案:


using System; namespace Space { public class Globe { public string GetColor() => "Green"; } } 

第二档:


 using System; namespace Space { public class Globe { public string GetColor() => "Blue"; } } 

尝试第一


由于根据问题的情况,我们应该使用外部程序集而不是项目,因此我们将相应地添加到项目的链接,就好像它们实际上只是库一样。 为此,首先编译所有项目,以便我们拥有所需的Globe1.dll和Globe2.dll。 然后在项目中添加指向它们的链接,如下所示:



现在尝试创建一个Globe类变量:



如您所见,IDE已经在现阶段警告我们,在理解应从何处获取我们需要的Globe类时存在问题。


乍一看,这种情况很典型,应该已经有现成的,用花岗岩浇铸的,可以应对烟囱溢出的产品。 事实证明,尚未为.NET Core提出针对此问题的解决方案。 还是我的Google让我失望了。 但是我设法在Stack Overflow上找到了一些有用的东西,我设法在Google上找到的唯一明智的出版物是在2006年,并且描述了经典版本.NET的类似情况 。 在这种情况下, 在NuGet项目的存储库讨论了非常类似的问题。


到目前为止,还没有很多有用的信息,但是仍然存在:


  • 在.NET的经典版本中,实现了别名机制
  • 根据规范,C# 支持在代码中使用别名

仍然需要了解如何在.NET Core中执行此操作。


不幸的是, 当前版本的文档很少谈论连接外部软件包/费用的可能性。 而且, csproj文件描述也绝不会说明创建别名的可能性。 但是,通过反复试验,我发现.NET Core中程序集的别名仍受支持。 内容如下:


 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> </PropertyGroup> <ItemGroup> <Reference Include="Globe1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <HintPath>..\Globe1\bin\Debug\netcoreapp2.1\Globe1.dll</HintPath> <Aliases>Lib1</Aliases> </Reference> <Reference Include="Globe2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <HintPath>..\Globe2\bin\Debug\netstandard2.0\Globe2.dll</HintPath> <Aliases>Lib2</Aliases> </Reference> </ItemGroup> </Project> 

现在剩下的工作是学习如何使用这些别名。 前面提到的extern关键字将帮助我们:


关于它的文档中写有以下内容:


在某些情况下,您可能需要引用具有相同完全限定类型名称的程序集的两个版本。 例如,您需要在一个应用程序中使用两个或多个版本的程序集。 使用外部程序集别名,可以将每个程序集的名称空间包含在由该别名命名的根级别名称空间内的包装器中,这使您可以在单个文件中使用它们。
...
每个extern别名的声明都会引入一个与全局名称空间匹配(但不在其内部)的根级别的名称空间。 因此,可以使用其全名来创建对每个程序集类型的明确引用,其全名是相应的名称空间别名。

这里的真相是不要忘记,在C#中也使用extern来声明具有来自非托管代码的外部实现的方法。 在这种情况下,extern通常与DllImport属性一起使用。 您可以在文档的相应部分中阅读有关此内容的更多信息。


因此,让我们尝试使用别名:


 extern alias Lib1; extern alias Lib2; using System; namespace Owl { ... public class SuperOwl { private Lib1::Space.Globe _firstGlobe; private Lib2::Space.Globe _secondGlobe; public void IntegrateGlobe(Lib1::Space.Globe globe) => _firstGlobe = globe; public void IntegrateGlobe(Lib2::Space.Globe globe) => _secondGlobe = globe; ... } } 

该代码甚至已经可以使用。 而且它可以正常工作。 但我仍然想使其更加优雅。 这可以通过非常简单的方式完成:


 extern alias Lib1; extern alias Lib2; using System; using SpaceOne=Lib1::Space; using SpaceTwo=Lib2::Space; 

现在,您可以使用通常的显而易见的语法:


 var globe1 = new SpaceOne.Globe() var globe2 = new SpaceTwo.Globe() 

测验


让我们测试一下猫头鹰:



如您所见,该代码运行良好,没有错误。 猫头鹰和地球仪的集成已成功完成!


→示例代码在GitHub上可用

Source: https://habr.com/ru/post/zh-CN421851/


All Articles