Есть ли способ сделать библиотеку .Net для Windows и Mac, с зависимыми от платформы ссылками?


12

Мы пытаемся разработать кроссплатформенное приложение для настольных компьютеров (Windows и Mac) с использованием C #. Приложение содержит огромное количество материалов, зависящих от платформы, и руководство нашей команды хочет, чтобы мы написали весь этот код на C #. Самый простой способ сделать это - написать обертки на C ++ и просто ссылаться на библиотеки в коде C # и позволить библиотекам C ++ работать с платформой ... увы, это не так.

Я пытаюсь настроить библиотеку с помощью Visual Studio для Mac. Я надеюсь, что смогу предоставить единый интерфейс в единой библиотеке для предоставления доступа к нативному тексту в речевую библиотеку на текущей платформе - желательно без двух сборок. Библиотека преобразования текста в речь на C # для Windows недоступна на Mac, поэтому у нас нет альтернативы, кроме как обеспечить какой-то мост самостоятельно.

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

Если мне нужно, то у меня будет один проект, который позволит мне написать две реализации. Я не нашел способа создать библиотечный проект в Visual Studio для Mac, который позволил бы мне сделать это, однако. Единственный тип проекта, который допускает несколько реализаций, кажется, требует, чтобы реализации были либо для iOS, либо для Android.


Какую среду .NET вы собираетесь использовать?
Эйфорическая

2
Ксамарин делает нечто подобное. Может быть с конфигами сборки?
Эван

2
Стоит отметить, что Visual Studio для Mac на самом деле не является визуальной студией, это просто обновленная студия Xamarin. Может быть, есть что-то в документах Xamarin?
Ричзилла

3
Лучшим подходом будут разные сборки ... Один для интерфейсов и общего кода, а другой для целевой платформы. Однако вы можете использовать несколько целей и использовать операторы #if для замены реализаций. Недостатком является то, что все, что не включено, не оценивается, поэтому может легко устареть.
Берин Лорич

Когда вы говорите о зависимых от платформы ссылках, вы говорите о нативном коде или коде C #?
Берин Лорич

Ответы:


4

У вас хороший вопрос. Вероятно, есть некоторые компромиссы с вашим решением. Окончательный ответ действительно зависит от того, что вы подразумеваете под зависимостью от платформы. Например, если вы запускаете процесс для запуска внешних приложений и просто переключаетесь между одним приложением, вы, вероятно, справитесь с этим без особых сложностей. Если вы говорите P / Invoke с нативными библиотеками, то нужно сделать еще немного. Однако, если вы связываетесь с библиотеками, которые существуют только на одной платформе, вам, вероятно, потребуется использовать несколько сборок.

Внешние приложения

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

В некоторых случаях это просто двоичный файл, скомпилированный для конкретной платформы, но имя исполняемого файла и все параметры определены одинаково. В этом случае это вопрос определения правильного исполняемого файла. Для приложения массового преобразования звука, которое может работать в Windows и Linux, у меня был статический инициализатор, разрешающий двоичное имя.

public class AudioProcessor
{
    private static readonly string AppName = "lame";
    private static readonly string FullAppPath;

    static AudioProcessor()
    {
        var platform = DetectPlatform();
        var architecture = Detect64or32Bits();

        FullAppPath = Path.combine(platform, architecture, AppName);
    }
}

Ничего особенного здесь. Просто хорошие старомодные занятия.

P / Invoke

P / Invoke немного сложнее. Суть в том, что вам нужно убедиться, что загружена правильная версия нативной библиотеки. На окнах вы бы P / Invoke SetDllDirectory(). Разные платформы могут не нуждаться в этом шаге. Так что здесь все может стать грязным. Возможно, вам придется использовать #ifоператоры, чтобы контролировать, какой вызов используется для управления разрешением пути к вашей библиотеке - особенно, если вы включаете его в свой пакет распространения.

Ссылки на совершенно разные платформенно-зависимые библиотеки

Здесь может пригодиться многоцелевой подход старой школы. Однако это приходит с большим количеством уродства. В те дни, когда некоторые проекты пытались использовать одну и ту же DLL-библиотеку для Silverlight, WPF и, возможно, UAP, вам приходилось компилировать приложение несколько раз с разными тегами компиляции. Проблема с каждой из вышеперечисленных платформ заключается в том, что, хотя они и используют одни и те же понятия, платформы достаточно различны, поэтому вам приходится обходить эти различия. Это где мы попадаем в ад #if.

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

# если ад

Вы можете включать и выключать разделы кода, используя #ifоператоры, чтобы эффективно обрабатывать незначительные различия между приложениями. На первый взгляд это звучит как хорошая идея. Я даже использовал его как средство для включения и выключения визуализации ограничивающего прямоугольника для отладки кода чертежа.

Проблема номер 1 в #ifтом, что ни один из отключенных кодов не анализируется анализатором. У вас могут быть скрытые синтаксические ошибки или, что еще хуже, логические ошибки, ожидающие перекомпиляции библиотеки. Это становится еще более проблематичным с рефакторингом кода. Что-то простое, например, переименование метода или изменение порядка параметров, обычно обрабатывается нормально, но поскольку анализатор никогда не оценивает ничего, отключенного #ifоператором, вы неожиданно испортили код, который не увидите, пока не перекомпилируете.

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

Мой предпочтительный метод

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

Одна базовая сборка NetStandard будет определять все интерфейсы и содержать весь общий код. Зависящие от платформы реализации были бы в отдельной сборке, которая добавляла бы функции, когда они включены.

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

Это единственный известный мне способ избежать #ifада и удовлетворить существенно другую среду.


Я сосу на C # (гавань использовала C ++ около 18 лет, а C # около половины дня, этого следовало ожидать). Эта новая конфигурация API ... не могли бы вы предоставить некоторые ссылки на это. Я не могу найти это самостоятельно.
яснее

2
docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/… Существует несколько пакетов Nuget для добавления дополнительных источников конфигурации. Например, из переменных среды, файлов JSON и т. Д.
Берин Лорич,

1
Это Microsoft.Extensions.Configurationнабор API.
Берин Лорич

1
Вот корневой проект github: github.com/aspnet/Configuration
Берин Лорич,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.