У вас гарне запитання. Напевно, є якісь компроміси з вашим рішенням. Кінцева відповідь дійсно залежить від того, що ви маєте на увазі під платформою. Наприклад, якщо ви запускаєте процес запуску зовнішніх програм і ви просто перемикаєтесь між однією програмою на іншу, ви, ймовірно, можете впоратися з цим без особливих ускладнень. Якщо ви розмовляєте з 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
пекла та задовольнити істотно інше середовище.