Хабрахабр

Проблематика совы и глобуса: подключение двух сборок с идентичными пространствами имен и названиями классов

В ходе беседы, дошли до обсуждения ситуации, когда в проект на . Сегодня вечером, с gelas завели разговор о том, как работают пакетные менеджеры на разных платформах. Поскольку . NET Core необходимо подключить две библиотеки, которые содержат классы с одинаковым названием в одинаковых пространствах имен. Что из этого вышло описано дальше NET Core я занимаюсь достаточно плотно, я захотел проверить, как можно решить подобную проблему.

Часто ли встречаются такие ситуации? Дисклеймер. NET, с подобной ситуацией в реальном проекте не приходилось сталкиваться ни разу. Мне за более чем 10 лет работы с . Но вот провести эксперимент было интересно.

На всякий случай уточню, что эксперимент я буду проводить используя:

  • macOS 10.13,
  • .NET Core SDK 2.1.302
  • Rider 2018.2

При этом доступ к исходному коду у нас нет, а декомпилировать это сборки, чтобы поменять в них неймспейсы, а потом скомпилировать обратно мы тоже не можем. Итак, смоделируем ситуацию, когда к нам попали две библиотеки, в которых есть необходимые нам классы, которые мы должны использовать в нашем проекте.

Подготовка эксперимента

В качестве совы у нас будет выступать проект с таргетом на netcoreapp2. И так, для начала подготовим одну сову и два глобуса. В качестве глобусов создадим два проекта, один из готорых будет также с таргетом на netcoreapp2. 1. 1, а второй на netstandard2.

В каждый проект поместим по классу Globe, которые будут находится в идентичных неймспейсах, но реализация при этому у них будет разная:

Первый файл:

using System; namespace Space

}

Вторй файл:

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

Попытка номер один

Для этого сначала скомпилируем все проекты, чтобы у нас появились нужные нам Globe1.dll и Globe2.dll. Поскольку по условиям задачи мы должны работать с внешними сборками, а не проектами, то добавим соответственно в проект ссылки как будто они действительно являются просто библиотеками. Затем добавим на них ссылки в проект в таком виде:

Теперь попробуем создать переменную класса Globe:

Как видим, уже на этом этапе IDE предупреждает нас о том, что есть проблема с пониманием того, откуда должен быть взят нужный нам класс Globe.

Как оказалось, для . Сначала кажется, что ситуация довольно типичная и на нее уже должны быть готовый, отлитый в граните, ответ на Stack Overflow. Либо мой Гугл меня подвел. NET Core решения подобной задачи пока еще предложено не было. NET. Но кое что полезное на Stack Overflow найти удалось.Единственная толковая публикация, которую удалось нагуглить — была за 2006 год и описывала подобную ситуацию для классической версии . При этом, весьма похожа проблема обсуждается в репозитории проекта NuGet.

Пока не очень много полезно информации, но она все же есть:

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 вводится дополнительное пространство имен корневого уровня, которое соответствует глобальному пространству имен (но не находится внутри него).

В этом случае extern обычно используется с атрибутом DllImport. Тут правда не стоит забывать о том, что extern также используется в C# для объявления метода с внешней реализацией из неуправляемого кода. Более детально об этом можно почитать в соответствующем разделе документации.

Итак, попробуем использовать наши псевдонимы:

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть