Чи є спосіб створити .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 isnt дійсно візуальна студія, це просто перероблена студія Xamarin. Може, є щось у документах Xamarin?
richzilla

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 трохи складніше. Суть полягає в тому, що вам потрібно забезпечити завантаження правильної версії рідної бібліотеки. На windows ви б 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 # близько півдня, це можна очікувати). Ця нова програма конфігурації ... чи можете ви надати деякі посилання на це. Я, здається, не можу знайти це самостійно.
Чіткіший

2
docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/… Існує кілька пакунків Nuget для додавання додаткових джерел конфігурації. Наприклад, зі змінних оточуючих середовищ, файлів JSON тощо
Berin Loritsch

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

1
Ось корінний проект github: github.com/aspnet/Configuration
Berin Loritsch
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.