
Ce soir, gelas a entamé une conversation sur le fonctionnement des gestionnaires de packages sur différentes plateformes. Au cours de la conversation, nous avons abordé la situation lorsque vous devez connecter deux bibliothèques au projet .NET Core qui contiennent des classes portant le même nom dans le même espace de noms. Étant donné que je fais un peu de .NET Core, je voulais vérifier comment ce problème peut être résolu. Ce qui en est arrivé est décrit plus en détail
Clause de non-responsabilité . De telles situations se produisent-elles fréquemment? Pendant plus de 10 ans de travail avec .NET, je n'ai jamais eu à faire face à une situation similaire dans un vrai projet. Mais l'expérience était intéressante.
Au cas où, je préciserais que je conduirai l'expérience en utilisant:
- macOS 10.13,
- SDK .NET Core 2.1.302
- Rider 2018.2
Donc, nous simulerons une situation où nous aurons deux bibliothèques qui ont les classes dont nous avons besoin que nous devons utiliser dans notre projet. Dans le même temps, nous n'avons pas accès au code source, mais nous ne pouvons pas décompiler les assemblys afin de changer leur espace de noms, puis nous ne pouvons pas non plus recompiler.
Préparation à l'expérience
Et donc, pour commencer, préparez un hibou et deux globes. En tant que hibou, nous aurons un projet ciblant netcoreapp2.1. Nous créerons deux projets en tant que globes, dont l'un sera également ciblé sur netcoreapp2.1 et le second sur netstandard2.0.

Dans chaque projet, nous plaçons la classe Globe, qui sera dans des espaces de noms identiques, mais ils auront des implémentations différentes:
Premier fichier:
using System; namespace Space { public class Globe { public string GetColor() => "Green"; } }
Deuxième fichier:
using System; namespace Space { public class Globe { public string GetColor() => "Blue"; } }
Tentative numéro un
Étant donné que, selon les conditions du problème, nous devons travailler avec des assemblys externes et non avec des projets, nous ajouterons des liens vers le projet en conséquence, comme s'ils n'étaient vraiment que des bibliothèques. Pour ce faire, compilez d'abord tous les projets afin d'avoir les fichiers Globe1.dll et Globe2.dll dont nous avons besoin. Ajoutez-leur ensuite des liens dans le projet comme suit:

Essayez maintenant de créer une variable de classe Globe:

Comme vous pouvez le voir, déjà à ce stade, l'IDE nous avertit qu'il y a un problème à comprendre d'où la classe Globe dont nous avons besoin doit être prise.
Au début, il semble que la situation soit assez typique et il devrait déjà y avoir une réponse prête à l'emploi, coulée en granit, à Stack Overflow. Il s'est avéré qu'aucune solution à ce problème n'a encore été proposée pour .NET Core. Ou mon Google m'a laissé tomber. Mais j'ai réussi à trouver quelque chose d'utile sur Stack Overflow. La seule publication raisonnable que j'ai réussi à google était en 2006 et décrivait une situation similaire pour la version classique de .NET. Dans ce cas, un problème très similaire est discuté dans le référentiel du projet NuGet .
Jusqu'à présent, pas beaucoup d'informations utiles, mais elles sont toujours là:
Il reste à comprendre comment procéder dans .NET Core.
Malheureusement, la version actuelle de la documentation parle assez modestement des possibilités de connexion de packages / frais externes. Et la description du fichier csproj ne fait en aucun cas la lumière sur la possibilité de créer des alias. Néanmoins, par essais et erreurs, j'ai pu découvrir que les alias des assemblys dans .NET Core sont toujours pris en charge. Et ils se présentent comme suit:
<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>
Reste maintenant à apprendre à utiliser ces alias. Le mot-clé extern mentionné précédemment nous aidera à cela:
Ce qui suit est écrit dans la documentation à ce sujet:
Dans certains cas, vous devrez peut-être référencer deux versions d'assemblys avec les mêmes noms de type complets. Par exemple, vous devez utiliser deux versions ou plus d'un assembly dans une application. À l'aide d'un alias d'assembly externe, vous pouvez inclure des espaces de noms pour chaque assembly dans un wrapper à l'intérieur des espaces de noms de niveau racine nommés par cet alias, ce qui vous permet de les utiliser dans un seul fichier.
...
Chaque déclaration d'un alias externe introduit un espace de noms de niveau racine supplémentaire qui correspond à l'espace de noms global (mais n'est pas à l'intérieur). Ainsi, des références aux types de chaque assembly sans ambiguïté peuvent être créées en utilisant leur nom complet, dont la racine est l'alias d'espace de noms correspondant.
La vérité ici n'est pas d'oublier que extern est également utilisé en C # pour déclarer une méthode avec une implémentation externe à partir de code non managé. Dans ce cas, extern est généralement utilisé avec l'attribut DllImport. Vous pouvez en savoir plus à ce sujet dans la section correspondante de la documentation .
Essayons donc d'utiliser nos alias:
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; ... } }
Ce code fonctionne déjà même. Et cela fonctionne correctement. Mais je veux quand même le rendre un peu plus élégant. Cela peut se faire de manière très simple:
extern alias Lib1; extern alias Lib2; using System; using SpaceOne=Lib1::Space; using SpaceTwo=Lib2::Space;
Vous pouvez maintenant utiliser la syntaxe habituelle et évidente:
var globe1 = new SpaceOne.Globe() var globe2 = new SpaceTwo.Globe()
Test
Testons notre chouette:

Comme vous pouvez le voir, le code a bien fonctionné et sans erreurs. L'intégration des hiboux et des globes est terminée avec succès!
→ Un exemple de code est disponible sur GitHub