Использование C/C++ Library с XamarinUse C/C++ libraries with Xamarin

ОбзорOverview

Xamarin позволяет разработчикам создавать кросс-платформенные собственные мобильные приложения с помощью Visual Studio.Xamarin enables developers to create cross-platform native mobile apps with Visual Studio. Как правило C# , привязки используются для предоставления разработчикам существующих компонентов платформы.Generally, C# bindings are used to expose existing platform components to developers. Однако бывают случаи, когда приложениям Xamarin требуется работать с существующими базой кода.However, there are times when Xamarin apps need to work with existing codebases. Иногда команды просто не имеют времени, бюджета или ресурсов для передачи больших, хорошо протестированных и оптимизированных баз кода C#.Sometimes teams simply don't have the time, budget, or resources to port a large, well-tested, and highly optimized codebase to C#.

Visual C++ для кросс-платформенной разработки мобильных приложений позволяет создаватьC++ код C# C/и в рамках одного решения, предлагая множество преимуществ, включая унифицированный процесс отладки.Visual C++ for cross-platform mobile development enables the C/C++ and C# code to be built as part of the same solution, offering many advantages including a unified debugging experience. Корпорация Майкрософт использовала CC++ /и Xamarin для доставки таких приложений, как сотовый телефон и PIX Camera.Microsoft has used C/C++ and Xamarin in this way to deliver apps such as Hyperlapse Mobile and Pix Camera.

Однако в некоторых случаях существует желание (или требование) для сохранения существующих C/C++ средств и процессов и сохранения кода библиотеки, отделенного от приложения, и рассматривая библиотеку как похожую на сторонние компоненты.However, in some cases there is a desire (or requirement) to keep existing C/C++ tools and processes in place and to keep the library code decoupled from the application, treating the library as if it were similar to a third-party component. В таких ситуациях задача не только предоставляет доступ к C# соответствующим элементам, но и управление библиотекой как зависимостью.In these situations, the challenge is not only exposing the relevant members to C# but managing the library as a dependency. И, конечно, автоматизируйте как можно большую часть этого процесса.And, of course, automating as much of this process as possible.

В этой публикации рассматривается высокоуровневый подход к этому сценарию и демонстрируется простой пример.This post outlines a high-level approach for this scenario and walks through a simple example.

ФоновыйBackground

В языкеC++ c/считается кросс-платформенный язык, но необходимо соблюдать осторожность, чтобы убедиться в том, что исходный код действительно является кросс-платформенным, используяC++ только C/поддерживается всеми целевыми компиляторами и содержащими небольшую или не имеющую условную платформу. код, зависящий от компилятора.C/C++ is considered a cross-platform language, but great care must be taken to ensure that the source code is indeed cross-platform, using only C/C++ supported by all target compilers and containing little or no conditionally-included platform or compiler-specific code.

В конечном итоге код должен успешно компилироваться и выполняться на всех целевых платформах, поэтому это сводится к унификации между платформами (и компиляторами), для которых нацелена цель.Ultimately the code must compile and run successfully on all target platforms therefore this boils down to the commonality across the platforms (and compilers) being targeted. Проблемы могут быть вызваны незначительными различиями между компиляторами, поэтому тщательное тестирование (желательно автоматически) на каждой целевой платформе становится все более важным.Issues may still arise from minor differences between compilers and so thorough testing (preferably automated) on each target platform becomes increasingly important.

Высокоуровневый подходHigh-level approach

На рисунке ниже представлен подход с четырьмя этапами, используемый для преобразования кодаC++ C/source в кросс-платформенную библиотеку Xamarin, которая совместно используется в NuGet, а затем используется в приложении Xamarin. Forms.The illustration below represents the four-stage approach used to transform C/C++ source code into a cross-platform Xamarin library that is shared via NuGet and then is consumed in a Xamarin.Forms app.

Высокоуровневый подход к использованию C/C++ с Xamarin

4 этапа:The 4 stages are:

  1. Компиляция кода C/C++ Source в машинные библиотеки, зависящие от платформы.Compiling the C/C++ source code into platform-specific native libraries.
  2. Создание оболочки для собственных библиотек с помощью решения Visual Studio.Wrapping the native libraries with a Visual Studio solution.
  3. Упаковка и отправка пакета NuGet для оболочки .NET.Packing and pushing a NuGet package for the .NET wrapper.
  4. Использование пакета NuGet из приложения Xamarin.Consuming the NuGet package from a Xamarin app.

Этап 1. Компиляция кода C/C++ Source в машинные библиотеки для конкретных платформStage 1: Compiling the C/C++ source code into platform-specific native libraries

Цель этого этапа — создать собственные библиотеки, которые могут вызываться C# оболочкой.The goal of this stage is to create native libraries that can be called by the C# wrapper. В зависимости от конкретной ситуации это может быть неуместным.This may or may not be relevant depending on your situation. Многие средства и процессы, которые могут быть включены в этот распространенный сценарий, выходят за рамки этой статьи.The many tools and processes that can be brought to bear in this common scenario are beyond the scope of this article. Ключевыми соображениями является обеспечение синхронизацииC++ C/CodeBase с любым собственным кодом оболочки, достаточным модульным тестированием и автоматизацией сборки.Key considerations are keeping the C/C++ codebase in sync with any native wrapper code, sufficient unit testing, and build automation.

Библиотеки в пошаговом руководстве были созданы с помощью Visual Studio Code с сопутствующим сценарием оболочки.The libraries in the walk-through were created using Visual Studio Code with an accompanying shell script. Расширенная версия этого пошагового руководства находится в репозитории GitHub для Mobile Cat , в котором подробно рассматривается эта часть примера.An extended version of this walk-through can be found in the Mobile CAT GitHub repository that discusses this part of the sample in greater depth. В этом случае машинные библиотеки обрабатываются как зависимость от сторонних производителей, однако этот этап проиллюстрирован для контекста.The native libraries are being treated as a third-party dependency in this case however this stage is illustrated for context.

Для простоты в этом пошаговом руководстве предназначено только подмножество архитектур.For simplicity, the walkthrough targets only a subset of architectures. Для iOS используется служебная программа липо для создания одного двоичного файла FAT из отдельных двоичных файлов конкретной архитектуры.For iOS, it uses the lipo utility to create a single fat binary from the individual architecture-specific binaries. Android будет использовать динамические двоичные файлы с расширением. Поэтому расширение и iOS будут использовать статический двоичный файл FAT с расширением. a.Android will use dynamic binaries with a .so extension and iOS will use a static fat binary with a .a extension.

Этап 2. Создание оболочек для собственных библиотек с помощью решения Visual StudioStage 2: Wrapping the native libraries with a Visual Studio solution

Следующий этап заключается в заключении в оболочку собственных библиотек, чтобы их можно было легко использовать из .NET.The next stage is to wrap the native libraries so that they are easily used from .NET. Это делается с помощью решения Visual Studio с четырьмя проектами.This is done with a Visual Studio solution with four projects. В общем проекте содержится общий код.A shared project contains the common code. Проекты, предназначенные для каждого из Xamarin. Android, Xamarin. iOS и .NET Standard позволяют ссылаться на библиотеку независимым от платформы образом.Projects targeting each of Xamarin.Android, Xamarin.iOS, and .NET Standard allow the library to be referenced in a platform-agnostic manner.

Программа-оболочка используетBait и хитрость, описываемые Пол Беттс.The wrapper uses 'the bait and switch trick,' described by Paul Betts. Это не единственный способ, но он упрощает ссылку на библиотеку и позволяет избежать необходимости явно управлять реализациями, зависящими от платформы, в самом приложении.This is not the only way, but it makes it easy to reference the library and it avoids the need to explicitly manage platform-specific implementations within the consuming application itself. Этот прием гарантирует, что целевые объекты (.NET Standard, Android, iOS) совместно используют одно и то же пространство имен, имя сборки и структуру класса.The trick is essentially ensuring that the targets (.NET Standard, Android, iOS) share the same namespace, assembly name, and class structure. Так как NuGet всегда предпочитает библиотеку для конкретной платформы, .NET Standardная версия никогда не будет использоваться во время выполнения.Since NuGet will always prefer a platform-specific library, the .NET Standard version is never used at runtime.

Большая часть работы на этом шаге посвящена использованию P/Invoke для вызова собственных методов библиотеки и управления ссылками на базовые объекты.Most of the work in this step will focus on using P/Invoke to call the native library methods and managing the references to the underlying objects. Задача заключается в предоставлении функциональных возможностей библиотеки потребителю при абстракции любой сложности.The goal is to expose the library’s functionality to the consumer while abstracting out any complexity. Разработчикам Xamarin. Forms не нужно иметь опыт работы с внутренними обработкой неуправляемой библиотеки.The Xamarin.Forms developers do not need to have working knowledge on the inner workings of the unmanaged library. Это должно быть похоже на использование любой другой управляемой C# библиотеки.It should feel like they are using any other managed C# library.

В конечном итоге, выходные данные этого этапа представляют собой набор библиотек .NET, по одному на целевой объект, а также документ nuspec, содержащий сведения, необходимые для сборки пакета на следующем шаге.Ultimately, the output of this stage is a set of .NET libraries, one per target, along with a nuspec document that contains the information required in order to build the package in the next step.

Этап 3. Упаковка и отправка пакета NuGet для оболочки .NETStage 3: Packing and pushing a NuGet package for the .NET wrapper

Третий этап — создание пакета NuGet с помощью артефактов сборки из предыдущего шага.The third stage is creating a NuGet package using the build artifacts from the previous step. Результатом этого шага является пакет NuGet, который можно использовать из приложения Xamarin.The outcome from this step is a NuGet package that can be consumed from a Xamarin app. В этом пошаговом руководстве используется локальный каталог, служащий веб-каналом NuGet.The walkthrough uses a local directory to serve as the NuGet feed. В рабочей среде этот шаг должен публиковать пакет в общедоступном или частном веб-канале NuGet и должен быть полностью автоматизирован.In production, this step should publish a package to a public or private NuGet feed and should be fully automated.

Этап 4. Использование пакета NuGet из приложения Xamarin. FormsStage 4: Consuming the NuGet package from a Xamarin.Forms app

Последним шагом является ссылка на пакет NuGet и его использование из приложения Xamarin. Forms.The final step is to reference and use the NuGet package from a Xamarin.Forms app. Для этого необходимо настроить канал NuGet в Visual Studio для использования канала, определенного на предыдущем шаге.This requires configuring the NuGet feed in Visual Studio to use the feed defined in the previous step.

После настройки веб-канала на него необходимо ссылаться из каждого проекта в приложении Xamarin. Forms в разных платформах.Once the feed is configured, the package needs to be referenced from each project in the cross-platform Xamarin.Forms app. ' Bait-and-Switch ' предоставляет идентичные интерфейсы, поэтому функциональность собственной библиотеки можно вызывать с помощью кода, определенного в одном месте.‘The bait-and-switch trick’ provides identical interfaces, so the native library functionality can be called using code defined in a single location.

Репозиторий исходного кода содержит список дополнительных материалов, в которых содержатся статьи о настройке частного канала NuGet в Azure DevOps и о том, как отправить пакет в этот веб-канал.The source code repository contains a list of further reading that includes articles on how to set up a private NuGet feed on Azure DevOps and how to push the package to that feed. Хотя при настройке локального каталога потребуется немного больше времени, этот тип веб-канала лучше использовать в среде коллективной разработки.While requiring a little more setup time than a local directory, this type of feed is better in a team development environment.

Пошаговое руководствоWalk-through

Указанные действия относятся к Visual Studio для Mac, но структура работает также в Visual Studio 2017 .The steps provided are specific to Visual Studio for Mac, but the structure works in Visual Studio 2017 as well.

Предварительные требованияPrerequisites

Чтобы отслеживать работу, разработчику потребуется:In order to follow along, the developer will need:

Примечание

Для развертывания приложений на устройстве iPhone требуется активная учетная запись разработчика Apple .An active Apple Developer Account is required in order to deploy apps to an iPhone.

Создание собственных библиотек (этап 1)Creating the native libraries (Stage 1)

Функциональные возможности собственной библиотеки основаны на примере из пошагового руководства. Создание и использование статической библиотеки (C++).The native library functionality is based on the example from Walkthrough: Creating and Using a Static Library (C++).

В этом пошаговом руководстве пропускается первый этап — создание собственных библиотек, так как в этом сценарии библиотека предоставляется как зависимость от стороннего разработчика.This walkthrough skips the first stage, building the native libraries, since the library is provided as a third-party dependency in this scenario. Предкомпилированные машинные библиотеки включены вместе с образцом кода или могут быть загружены напрямую.The precompiled native libraries are included alongside the sample code or can be downloaded directly.

Работа с собственной библиотекойWorking with the native library

Исходный пример MathFuncsLib включает один класс с именем MyMathFuncs со следующим определением:The original MathFuncsLib example includes a single class called MyMathFuncs with the following definition:

namespace MathFuncs
{
    class MyMathFuncs
    {
    public:
        double Add(double a, double b);
        double Subtract(double a, double b);
        double Multiply(double a, double b);
        double Divide(double a, double b);
    };
}

Дополнительный класс определяет функции-оболочки, позволяющие потребителю .NET создавать, удалять и взаимодействовать с базовым собственным MyMathFuncs классом.An additional class defines wrapper functions that allow a .NET consumer to create, dispose, and interact with the underlying native MyMathFuncs class.

#include "MyMathFuncs.h"
using namespace MathFuncs;

extern "C" {
    MyMathFuncs* CreateMyMathFuncsClass();
    void DisposeMyMathFuncsClass(MyMathFuncs* ptr);
    double MyMathFuncsAdd(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsSubtract(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsMultiply(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsDivide(MyMathFuncs *ptr, double a, double b);
}

Эти функции-оболочки будут использоваться на стороне Xamarin .It will be these wrapper functions that are used on the Xamarin side.

Создание оболочки для собственной библиотеки (этап 2)Wrapping the native library (Stage 2)

Для этого этапа требуются предварительно скомпилированные библиотеки , описанные в предыдущем разделе.This stage requires the precompiled libraries described in the previous section.

Создание решения Visual StudioCreating the Visual Studio solution

  1. В Visual Studio для Macщелкните создать проект (на странице приветствия) или новое решение (в меню файл ).In Visual Studio for Mac, click New Project (from the Welcome Page) or New Solution (from the File menu).

  2. В окне Новый проект выберите общий проект (из многоплатформенной > Библиотека), а затем нажмите кнопку Далее.From the New Project window, choose Shared Project (from within Multiplatform > Library) and then click Next.

  3. Обновите следующие поля и нажмите кнопку создать.Update the following fields then click Create:

    • Имя проекта: Масфункс. SharedProject Name: MathFuncs.Shared
    • Имя решения: масфунксSolution Name: MathFuncs
    • Расположение: Использовать расположение для сохранения по умолчанию (или выбрать альтернативу)Location: Use the default save location (or pick an alternative)
    • Создайте проект в каталоге решения: Установить значение "Проверено"Create a project within the solution directory: Set this to checked
  4. В Обозреватель решенийдважды щелкните проект масфункс. Shared и перейдите к разделу Основные параметры.From Solution Explorer, double-click on the MathFuncs.Shared project and navigate to Main Settings.

  5. Удалить . Предоставлен общий доступ из пространства имен по умолчанию , поэтому он имеет значение масфункс , а затем нажмите кнопку ОК.Remove .Shared from the Default Namespace so it is set to MathFuncs only, then click OK.

  6. Откройте MyClass.CS (созданный шаблоном), а затем переименуйте класс и имя файла в мимасфунксвраппер и измените пространство имен на масфункс.Open MyClass.cs (created by the template), then rename both the class and the filename to MyMathFuncsWrapper and change the namespace to MathFuncs.

  7. Щелкните решение масфункс, а затем выберите Добавить новый проект... в меню Добавить .CONTROL + CLICK on the solution MathFuncs, then choose Add New Project... from the Add menu.

  8. В окне Новый проект выберите .NET Standard библиотекамногоплатформенной библиотеке > ) и нажмите кнопку Далее.From the New Project window, choose .NET Standard Library (from within Multiplatform > Library) and then click Next.

  9. Выберите .NET Standard 2,0 и нажмите кнопку Далее.Choose .NET Standard 2.0 and then click Next.

  10. Обновите следующие поля и нажмите кнопку создать.Update the following fields then click Create:

    • Имя проекта: Масфункс. StandardProject Name: MathFuncs.Standard
    • Расположение: Использовать то же расположение для сохранения, что и для общего проектаLocation: Use the same save location as the shared project
  11. В Обозреватель решенийдважды щелкните проект масфункс. Standard .From Solution Explorer, double-click on the MathFuncs.Standard project.

  12. Перейдите к главному параметрам, а затем обновите пространство имен по умолчанию на масфункс.Navigate to Main Settings, then update Default Namespace to MathFuncs.

  13. Перейдите к параметрам вывода , а затем обновите имя сборки на масфункс.Navigate to the Output settings, then update Assembly name to MathFuncs.

  14. Перейдите к параметрам компилятора , измените конфигурацию на выпуск, установив отладочную информацию в SYMBOLS , а затем нажмите кнопку ОК.Navigate to the Compiler settings, change the Configuration to Release, setting Debug information to Symbols Only then click OK.

  15. Удаление Class1.CS/Getting, запущенного из проекта (если один из них был включен в состав шаблона).Delete Class1.cs/Getting Started from the project (if one of these has been included as part of the template).

  16. Нажмите кнопку "Управление " и щелкните папку " зависимости проекта/ссылки ", а затем выберите изменить ссылки.CONTROL + CLICK on the project Dependencies/References folder, then choose Edit References.

  17. Выберите масфункс. Shared на вкладке проекты , а затем нажмите кнопку ОК.Select MathFuncs.Shared from the Projects tab, then click OK.

  18. Повторите шаги 7-17 (шаг 9 пропускается), используя следующие конфигурации:Repeat steps 7-17 (ignoring step 9) using the following configurations:

    ИМЯ ПРОЕКТАPROJECT NAME ИМЯ ШАБЛОНАTEMPLATE NAME МЕНЮ "СОЗДАТЬ ПРОЕКТ"NEW PROJECT MENU
    Масфункс. AndroidMathFuncs.Android Библиотека классовClass Library Библиотека > AndroidAndroid > Library
    Масфункс. iOSMathFuncs.iOS Библиотека привязкиBinding Library Библиотека > iOSiOS > Library
  19. В Обозреватель решенийдважды щелкните проект масфункс. Android , а затем перейдите к параметрам компилятора .From Solution Explorer, double-click on the MathFuncs.Android project, then navigate to the Compiler settings.

  20. Если Конфигурация настроена на отладку, измените параметр определить символы , чтобы включить Android; .With the Configuration set to Debug, edit Define Symbols to include Android;.

  21. Измените конфигурацию на выпуск, а затем измените Определение символов , чтобы также включить Android.Change the Configuration to Release, then edit Define Symbols to also include Android;.

  22. Повторите шаги 19-20 для масфункс. iOS, а затем измените Определение символов , чтобы включить iOS , а не Android; в обоих случаях.Repeat steps 19-20 for MathFuncs.iOS, editing Define Symbols to include iOS; instead of Android; in both cases.

  23. Постройте решение в конфигурации выпуска (Control + Command + B) и убедитесь, что все три выходные сборки (Android, iOS, .NET Standard) (в соответствующих папках Bin проекта) имеют одно и то же имя масфункс. dll.Build the solution in Release configuration (CONTROL + COMMAND + B) and validate that all three output assemblies (Android, iOS, .NET Standard) (in the respective project bin folders) share the same name MathFuncs.dll.

На этом этапе решение должно иметь три цели: один каждый для Android, iOS и .NET Standard и общий проект, на который ссылается каждая из трех целей.At this stage, the solution should have three targets, one apiece for Android, iOS and .NET Standard, and a shared project that is referenced by each of the three targets. Они должны быть настроены на использование одного и того же пространства имен по умолчанию и выходных сборок с тем же именем.These should be configured to use the same default namespace and output assemblies with the same name. Это необходимо для подхода "Bait and Switch", упомянутого ранее.This is necessary for the 'bait and switch' approach mentioned previously.

Добавление собственных библиотекAdding the native libraries

Процесс добавления собственных библиотек в решение-оболочку незначительно различается для Android и iOS.The process of adding the native libraries to the wrapper solution varies slightly between Android and iOS.

Собственные ссылки для Масфункс. AndroidNative references for MathFuncs.Android

  1. Щелкните проект масфункс. Android , а затем в меню Добавить выберите пункт создать папку с именем библиотеки.CONTROL + CLICK on the MathFuncs.Android project, then choose New Folder from the Add menu naming it libs.

  2. Для каждого интерфейса ABI (двоичный интерфейс приложения) щелкните папку библиотеки , а затем в меню Добавить выберите пункт создать папку , настроив его после соответствующего ABI.For each ABI (Application Binary Interface), CONTROL + CLICK on the libs folder, then choose New Folder from the Add menu, naming it after that respective ABI. В этом случае:In this case:

    • arm64-v8aarm64-v8a
    • armeabi-v7a;armeabi-v7a
    • x86x86
    • x86_64x86_64

    Примечание

    Более подробные сведения см. в разделе архитектуры и ЦП в Руководстве разработчика NDK, в частности в разделе об адресации машинного кода в пакетах приложений.For a more detailed overview, see the Architectures and CPUs topic from the NDK developer guide, specifically the section on addressing native code in app packages.

  3. Проверьте структуру папок:Verify the folder structure:

    - lib
        - arm64-v8a
        - armeabi-v7a
        - x86
        - x86_64
    
  4. Добавьте соответствующие библиотеки . Таким образом в каждую из папок ABI на основе следующего сопоставления:Add the corresponding .so libraries to each of the ABI folders based on the following mapping:

    arm64-v8a: библиотеки, Android и arm64arm64-v8a: libs/Android/arm64

    ARMEABI-V7A: библиотеки, Android и ARMarmeabi-v7a: libs/Android/arm

    x86: библиотеки/Android/x86x86: libs/Android/x86

    x86_64: библиотеки, Android и x86_64x86_64: libs/Android/x86_64

    Примечание

    Чтобы добавить файлы, щелкните папку, представляющую соответствующий ABI, и выберите Добавить файлы... в меню " Добавить ".To add files, CONTROL + CLICK on the folder representing the respective ABI, then choose Add Files... from the Add menu. Выберите соответствующую библиотеку (из каталога прекомпиледлибс ), а затем нажмите кнопку Открыть , а затем нажмите кнопку ОК , чтобы скопировать файл в каталог.Choose the appropriate library (from the PrecompiledLibs directory) then click Open and then click OK leaving the default option to Copy the file to the directory.

  5. Для каждого из файлов нажмите кнопку " Управление " и выберите параметр Ембеддеднативелибрари в меню " действие сборки ".For each of the .so files, CONTROL + CLICK then choose the EmbeddedNativeLibrary option from the Build Action menu.

Теперь папка библиотеки должна выглядеть следующим образом:Now the libs folder should appear as follows:

- lib
    - arm64-v8a
        - libMathFuncs.so
    - armeabi-v7a
        - libMathFuncs.so
    - x86
        - libMathFuncs.so
    - x86_64
        - libMathFuncs.so

Собственные ссылки для Масфункс. iOSNative references for MathFuncs.iOS

  1. Щелкните проект масфункс. iOS и выберите Добавить встроенную ссылку в меню Добавить .CONTROL + CLICK on the MathFuncs.iOS project, then choose Add Native Reference from the Add menu.

  2. Выберите либмасфункс. Library (из библиотек или iOS в каталоге прекомпиледлибс ) и нажмите кнопку Открыть .Choose the libMathFuncs.a library (from libs/ios under the PrecompiledLibs directory) then click Open

  3. Щелкните файл либмасфункс (в папке " машинные ссылки ", а затем выберите в меню пункт " Свойства ").CONTROL + CLICK on the libMathFuncs file (within the Native References folder, then choose the Properties option from the menu

  4. Настройте свойства встроенной ссылки , чтобы они были отмечены (на панели свойств отображается значок деления):Configure the Native Reference properties so they are checked (showing a tick icon) in the Properties Pad:

    • Принудительная ЗагрузкаForce Load
    • РекомендуетсяC++Is C++
    • Интеллектуальная связьSmart Link

    Примечание

    Использование типа проекта библиотеки привязки вместе со встроенной ссылкой внедряет статическую библиотеку и позволяет автоматически связывать ее с приложением Xamarin. iOS, которое ссылается на него (даже если оно включено через пакет NuGet).Using a binding library project type along with a native reference embeds the static library and enables it to be automatically linked with the Xamarin.iOS app that references it (even when it is included via a NuGet package).

  5. Откройте ApiDefinition.CS, удалив шаблонный код с комментарием (не выкрывая MathFuncs только пространство имен), а затем выполните то же действие для structs.CS .Open ApiDefinition.cs, deleting the templated commented code (leaving only the MathFuncs namespace), then perform the same step for Structs.cs

    Примечание

    Проект библиотеки привязки требует, чтобы эти файлы (с действиями сборки обжкбиндингапидефинитион и обжкбиндингкоресаурце ) были созданы.A Binding library project requires these files (with the ObjCBindingApiDefinition and ObjCBindingCoreSource build actions) in order to build. Однако мы напишем код, чтобы вызвать нашу собственную библиотеку за пределами этих файлов таким образом, который можно совместно использовать в целевых объектах библиотеки Android и iOS с помощью стандартного метода P/Invoke.However, we will write the code, to call our native library, outside of these files in a way that can be shared between both Android and iOS library targets using standard P/Invoke.

Написание кода управляемой библиотекиWriting the managed library code

Теперь напишите C# код для вызова собственной библиотеки.Now, write the C# code to call the native library. Целью является скрытие всех базовых сложностей.The goal is to hide any underlying complexity. Потребителю не нужен никакой опыт работы с внутренними библиотеками или понятиями P/Invoke.The consumer should not need any working knowledge of the native library internals or of P/Invoke concepts.

Создание SafeHandleCreating a SafeHandle

  1. Щелкните проект масфункс. Shared , а затем выберите Добавить файл... в меню Добавить .CONTROL + CLICK on the MathFuncs.Shared project, then choose Add File... from the Add menu.

  2. В окне новый файл выберите пустой класс , назовите его мимасфункссафехандле и нажмите кнопку создать .Choose Empty Class from the New File window, name it MyMathFuncsSafeHandle and then click New

  3. Реализуйте класс мимасфункссафехандле :Implement the MyMathFuncsSafeHandle class:

    using System;
    using Microsoft.Win32.SafeHandles;
    
    namespace MathFuncs
    {
        internal class MyMathFuncsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public MyMathFuncsSafeHandle() : base(true) { }
    
            public IntPtr Ptr => this.handle;
    
            protected override bool ReleaseHandle()
            {
                // TODO: Release the handle here
                return true;
            }
        }
    }
    

    Примечание

    SafeHandle — это предпочтительный способ работы с неуправляемыми ресурсами в управляемом коде.A SafeHandle is the preferred way to work with unmanaged resources in managed code. Это абстрагирует множество стандартного кода, связанного с критическим завершением и жизненным циклом объектов.This abstracts away a lot of boilerplate code related to critical finalization and object lifecycle. Владелец этого маркера может впоследствии обрабатывать его как любой другой управляемый ресурс и не должен реализовывать полный шаблон для уничтожения.The owner of this handle can subsequently treat it like any other managed resource and will not have to implement the full Disposable pattern.

Создание внутреннего класса-оболочкиCreating the internal wrapper class

  1. Откройте MyMathFuncsWrapper.CS, изменив его на внутренний статический классOpen MyMathFuncsWrapper.cs, changing it to an internal static class

    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
        }
    }
    
  2. В том же файле добавьте в класс следующую условную инструкцию:In the same file, add the following conditional statement to the class:

    #if Android
        const string DllName = "libMathFuncs.so";
    #else
        const string DllName = "__Internal";
    #endif
    

    Примечание

    Это задает значение константы dllname в зависимости от того, строится ли библиотека для Android или iOS.This sets the DllName constant value based on whether the library is being built for Android or iOS. Это позволяет обращаться к различным соглашениям об именовании, используемым каждой соответствующей платформой, но также к типу библиотеки, используемой в этом случае.This is to address the different naming conventions used by each respective platform but also the type of library being used in this case. Android использует динамическую библиотеку, поэтому в нем должно быть имя файла, включая расширение.Android is using a dynamic library and so expects a filename including extension. Для iOS требуется " __Internal", так как используется статическая библиотека.For iOS, '__Internal' is required since we are using a static library.

  3. Добавьте ссылку на System. Runtime. InteropServices в начало файла MyMathFuncsWrapper.CSAdd a reference to System.Runtime.InteropServices at the top of the MyMathFuncsWrapper.cs file

    using System.Runtime.InteropServices;
    
  4. Добавьте методы-оболочки для создания и удаления класса MyMathFuncs :Add the wrapper methods to handle the creation and disposal of the MyMathFuncs class:

    [DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")]
    internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs();
    
    [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")]
    internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
    

    Примечание

    Мы передаем нашу константу dllname в атрибут DllImport вместе с точкой входа , которая явно сообщает среде выполнения .NET имя функции для вызова в этой библиотеке.We are passing in our constant DllName to the DllImport attribute along with the EntryPoint which explicitly tells the .NET runtime the name of the function to call within that library. Технически не нужно предоставлять значение EntryPoint , если имена управляемых методов идентичны неуправляемым.Technically, we do not need to provide the EntryPoint value if our managed method names were identical to the unmanaged one. Если он не указан, вместо него в качестве точки входа будет использоваться имя управляемого метода.If one is not provided, the managed method name would be used as the EntryPoint instead. Однако лучше быть явным.However, it is better to be explicit.

  5. Добавьте методы-оболочки, чтобы позволить нам работать с классом MyMathFuncs , используя следующий код:Add the wrapper methods to enable us to work with the MyMathFuncs class using the following code:

    [DllImport(DllName, EntryPoint = "MyMathFuncsAdd")]
    internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")]
    internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")]
    internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")]
    internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b);
    

    Примечание

    В этом примере мы используем простые типы для параметров.We're using simple types for the parameters in this example. Поскольку маршалинг является побитовой копией в этом случае, для нашей части не требуется дополнительная работа.Since marshalling is a bitwise-copy in this case it requires no additional work on our part. Также обратите внимание на использование класса мимасфункссафехандле вместо стандартного IntPtr.Also notice the use of the MyMathFuncsSafeHandle class instead of the standard IntPtr. IntPtr автоматически сопоставляется с SafeHandle как часть процесса маршалинга.The IntPtr is automatically mapped to the SafeHandle as part of the marshalling process.

  6. Убедитесь, что завершенный класс мимасфунксвраппер выглядит следующим образом:Verify that the finished MyMathFuncsWrapper class appears as below:

    using System.Runtime.InteropServices;
    
    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
            #if Android
                const string DllName = "libMathFuncs.so";
            #else
                const string DllName = "__Internal";
            #endif
    
            [DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")]
            internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs();
    
            [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")]
            internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsAdd")]
            internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")]
            internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")]
            internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")]
            internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b);
        }
    }
    

Завершение работы класса МимасфункссафехандлеCompleting the MyMathFuncsSafeHandle class

  1. Откройте класс мимасфункссафехандле , перейдите к заполнительу комментария TODO в методе ReleaseHandle :Open the MyMathFuncsSafeHandle class, navigate to the placeholder TODO comment within the ReleaseHandle method:

    // TODO: Release the handle here
    
  2. Замените строку TODO :Replace the TODO line:

    MyMathFuncsWrapper.DisposeMyMathFuncs(this);
    

Написание класса MyMathFuncsWriting the MyMathFuncs class

Теперь, когда оболочка завершена, создайте класс MyMathFuncs, который будет управлять ссылкой на неуправляемый объект C++ MyMathFuncs.Now that the wrapper is complete, create a MyMathFuncs class that will manage the reference to the unmanaged C++ MyMathFuncs object.

  1. Щелкните проект масфункс. Shared , а затем выберите Добавить файл... в меню Добавить .CONTROL + CLICK on the MathFuncs.Shared project, then choose Add File... from the Add menu.

  2. В окне новый файл выберите пустой класс , назовите его MyMathFuncs и нажмите кнопку создать .Choose Empty Class from the New File window, name it MyMathFuncs and then click New

  3. Добавьте следующие члены в класс MyMathFuncs :Add the following members to the MyMathFuncs class:

    readonly MyMathFuncsSafeHandle handle;
    
  4. Реализуйте конструктор для класса, чтобы он создавал и сохранял обработчик для собственного объекта MyMathFuncs при создании экземпляра класса:Implement the constructor for the class so it creates and stores a handle to the native MyMathFuncs object when the class is instantiated:

    public MyMathFuncs()
    {
        handle = MyMathFuncsWrapper.CreateMyMathFuncs();
    }
    
  5. Реализуйте интерфейс IDisposable , используя следующий код:Implement the IDisposable interface using the following code:

    public class MyMathFuncs : IDisposable
    {
        ...
    
        protected virtual void Dispose(bool disposing)
        {
            if (handle != null && !handle.IsInvalid)
                handle.Dispose();
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        // ...
    }
    
  6. Реализуйте методы MyMathFuncs с помощью класса мимасфунксвраппер , чтобы выполнить реальную работу за счет передачи указателя, сохраненного в базовый неуправляемый объект.Implement the MyMathFuncs methods using the MyMathFuncsWrapper class to perform the real work under the hood by passing in the pointer we have stored to the underlying unmanaged object. Код должен выглядеть следующим образом:The code should be as follows:

    public double Add(double a, double b)
    {
        return MyMathFuncsWrapper.Add(handle, a, b);
    }
    
    public double Subtract(double a, double b)
    {
        return MyMathFuncsWrapper.Subtract(handle, a, b);
    }
    
    public double Multiply(double a, double b)
    {
        return MyMathFuncsWrapper.Multiply(handle, a, b);
    }
    
    public double Divide(double a, double b)
    {
        return MyMathFuncsWrapper.Divide(handle, a, b);
    }
    

Создание nuspecCreating the nuspec

Чтобы библиотека была упакована и распространена с помощью NuGet, решению требуется файл nuspec .In order to have the library packaged and distributed via NuGet, the solution needs a nuspec file. Это определит, какие из результирующих сборок будут включаться для каждой поддерживаемой платформы.This will identify which of the resulting assemblies will be included for each supported platform.

  1. Щелкните решение масфункс, а затем выберите Добавить папку решения в меню Добавить имя солутионитемс.CONTROL + CLICK on the solution MathFuncs, then choose Add Solution Folder from the Add menu naming it SolutionItems.

  2. Щелкните папку солутионитемс , а затем выберите создать файл... в меню Добавить .CONTROL + CLICK on the SolutionItems folder, then choose New File... from the Add menu.

  3. Выберите пустой XML-файл в окне новый файл , назовите его масфункс. nuspec и нажмите кнопку создать.Choose Empty XML File from the New File window, name it MathFuncs.nuspec and then click New.

  4. Обновите масфункс. nuspec , указав базовые метаданные пакета, которые будут отображаться для потребителя NuGet .Update MathFuncs.nuspec with the basic package metadata to be displayed to the NuGet consumer. Например:For example:

    <?xml version="1.0"?>
    <package>
        <metadata>
            <id>MathFuncs</id>
            <version>$version$</version>
            <authors>Microsoft Mobile Customer Advisory Team</authors>
            <description>Sample C++ Wrapper Library</description>
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <copyright>Copyright 2018</copyright>
        </metadata>
    </package>
    

    Примечание

    Дополнительные сведения о схеме, используемой для этого манифеста, см. в справочном документе по nuspec.See the nuspec reference document for further detail on the schema used for this manifest.

  5. Добавьте элемент как дочерний <package> элемент элемента (как показано ниже <metadata>), определяя каждый файл с помощью отдельного <file> элемента: <files>Add a <files> element as a child of the <package> element (just below <metadata>), identifying each file with a separate <file> element:

    <files>
    
        <!-- Android -->
    
        <!-- iOS -->
    
        <!-- netstandard2.0 -->
    
    </files>
    

    Примечание

    При установке пакета в проект и наличии нескольких сборок, указанных одним и тем же именем, NuGet эффективно выбирает сборку, наиболее подданную для данной платформы.When a package is installed into a project, and where there are multiple assemblies specified by the same name, NuGet will effectively choose the assembly that is most specific to the given platform.

  6. Добавьте элементы для сборок Android: <file>Add the <file> elements for the Android assemblies:

    <file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" />
    <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" />
    
  7. Добавьте элементы для сборок iOS: <file>Add the <file> elements for the iOS assemblies:

    <file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" />
    <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" />
    
  8. Добавьте элементы для сборок netstandard 2.0: <file>Add the <file> elements for the netstandard2.0 assemblies:

    <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" />
    <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" />
    
  9. Проверьте манифест nuspec :Verify the nuspec manifest:

    <?xml version="1.0"?>
    <package>
    <metadata>
        <id>MathFuncs</id>
        <version>$version$</version>
        <authors>Microsoft Mobile Customer Advisory Team</authors>
        <description>Sample C++ Wrapper Library</description>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <copyright>Copyright 2018</copyright>
    </metadata>
    <files>
    
        <!-- Android -->
        <file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" />
        <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" />
    
        <!-- iOS -->
        <file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" />
        <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" />
    
        <!-- netstandard2.0 -->
        <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" />
        <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" />
    
    </files>
    </package>
    

    Примечание

    Этот файл указывает выходные пути сборки из сборки выпуска , поэтому не забудьте построить решение, используя эту конфигурацию.This file specifies the assembly output paths from a Release build, so be sure to build the solution using that configuration.

На этом этапе решение содержит 3 сборки .NET и вспомогательный манифест nuspec .At this point, the solution contains 3 .NET assemblies and a supporting nuspec manifest.

Распространение оболочки .NET с помощью NuGetDistributing the .NET wrapper with NuGet

Следующим шагом является упаковка и распространение пакета NuGet, чтобы его можно было легко использовать в приложении и управлять им как зависимость.The next step is to package and distribute the NuGet package so it may be easily consumed by the app and managed as a dependency. Перенос и потребление можно выполнять в рамках одного решения, но распространять библиотеку с помощью средств NuGet при разделении и позволяет управлять этими кодовыми библиотеками независимо друг от друга.The wrapping and consumption could all be done within a single solution, but distributing the library via NuGet aids in decoupling and enables us to manage these codebases independently.

Подготовка каталога локальных пакетовPreparing a local packages directory

Самая простая форма веб-канала NuGet — это локальный каталог:The simplest form of NuGet feed is a local directory:

  1. В окне Finderперейдите к удобному каталогу.In Finder, navigate to a convenient directory. Например, /Users.For example, /Users.
  2. В меню файл выберите пункт создать папку , указав Понятное имя, например Local-NuGet-Feed.Choose New Folder from the File menu, providing a meaningful name such as local-nuget-feed.

Создание пакетаCreating the package

  1. Задайте для конфигурации сборки значение Releaseи выполните сборку с помощью команды + B.Set the Build Configuration to Release, and execute a build using COMMAND + B.

  2. Откройте окно терминала и перейдите в папку, содержащую файл nuspec .Open Terminal and change directory to the folder containing the nuspec file.

  3. В окне терминалавыполните команду NuGet Pack , указав файл nuspec , версию (например, 1.0.0) и OutputDirectory , используя папку, созданную на предыдущем шаге, то есть локальный — NuGet — веб-канал.In Terminal, execute the nuget pack command specifying the nuspec file, the Version (for example, 1.0.0), and the OutputDirectory using the folder created in the previous step, that is, local-nuget-feed. Например:For example:

    nuget pack MathFuncs.nuspec -Version 1.0.0 -OutputDirectory ~/local-nuget-feed
    
  4. Убедитесь в том, что масфункс. 1.0.0. nupkg создан в каталоге Local-веб-канала .Confirm that MathFuncs.1.0.0.nupkg has been created in the local-nuget-feed directory.

ИСПОЛЬЗУЕМЫХ Использование частного веб-канала NuGet с Azure DevOps[OPTIONAL] Using a private NuGet feed with Azure DevOps

Более надежная методика описана в статье Начало работы с пакетами NuGet в Azure DevOps, в которой показано, как создать частный веб-канал и отправить пакет (созданный на предыдущем шаге) в этот канал.A more robust technique is described in Get started with NuGet packages in Azure DevOps, which shows how to create a private feed and push the package (generated in the previous step) to that feed.

Это идеальный вариант для полной автоматизации рабочего процесса, например с помощью Azure pipelines.It is ideal to have this workflow fully automated, for example using Azure Pipelines. Дополнительные сведения см. в статье Приступая к работе с Azure pipelines.For more information, see Get started with Azure Pipelines.

Использование оболочки .NET из приложения Xamarin. FormsConsuming the .NET wrapper from a Xamarin.Forms app

Для выполнения этого пошагового руководства Создайте приложение Xamarin. Forms для использования пакета, только что опубликованного в локальном веб-канале NuGet .To complete the walkthrough, create a Xamarin.Forms app to consume the package just published to the local NuGet feed.

Создание проекта Xamarin. FormsCreating the Xamarin.Forms project

  1. Откройте новый экземпляр Visual Studio для Mac.Open a new instance of Visual Studio for Mac. Это можно сделать с терминала:This can be done from Terminal:

    open -n -a "Visual Studio"
    
  2. В Visual Studio для Macщелкните создать проект (на странице приветствия) или новое решение (в меню файл ).In Visual Studio for Mac, click New Project (from the Welcome Page) or New Solution (from the File menu).

  3. В окне Новый проект выберите пустое приложение форммногоплатформенном > приложении) и нажмите кнопку Далее.From the New Project window, choose Blank Forms App (from within Multiplatform > App) and then click Next.

  4. Обновите следующие поля и нажмите кнопку Далее.Update the following fields then click Next:

    • Имя приложения: Масфунксапп.App Name: MathFuncsApp.
    • Идентификатор организации: Используйте обратные пространства имен, например com. {your_org} .Organization Identifier: Use a reverse namespace, for example, com.{your_org}.
    • Целевые платформы: Используйте значения по умолчанию (цели Android и iOS).Target Platforms: Use the default (both Android and iOS targets).
    • Общий код: Присвойте этому параметру значение .NET Standard ("Общая библиотека"), но помимо области действия этого пошагового руководства.Shared Code: Set this to .NET Standard (a “Shared Library” solution is possible, but beyond the scope of this walkthrough).
  5. Обновите следующие поля и нажмите кнопку создать.Update the following fields then click Create:

    • Имя проекта: Масфунксапп.Project Name: MathFuncsApp.
    • Имя решения: Масфунксапп.Solution Name: MathFuncsApp.
    • Расположение: Используйте расположение для сохранения по умолчанию (или выберите альтернативный вариант).Location: Use the default save location (or pick an alternative).
  6. В Обозреватель решенийщелкните целевой объект (масфунксапп. Android или масфункс. iOS) для первоначального тестирования, а затем выберите Назначить запускаемым проектом.In Solution Explorer, CONTROL + CLICK on the target (MathFuncsApp.Android or MathFuncs.iOS) for initial testing, then choose Set As Startup Project.

  7. Выберите предпочитаемое устройство илиэмулятор симулятора/.Choose the preferred device or Simulator/Emulator.

  8. Запустите решение (команда + Return), чтобы убедиться в том, что шаблон проекта Xamarin. Forms строится и работает нормально.Run the solution (COMMAND + RETURN) to validate that the templated Xamarin.Forms project builds and runs okay.

    Примечание

    iOS (в частности, симулятор) обычно имеет самое быстрое время сборки или развертывания.iOS (specifically the simulator) tends to have the fastest build/deploy time.

Добавление локального веб-канала NuGet в конфигурацию NuGetAdding the local NuGet feed to the NuGet configuration

  1. В Visual Studioвыберите предпочтения (в меню Visual Studio ).In Visual Studio, choose Preferences (from the Visual Studio menu).

  2. Выберите источники в разделе NuGet , а затем нажмите кнопку добавить.Choose Sources from under the NuGet section, then click Add.

  3. Обновите следующие поля и нажмите кнопку Добавить источник:Update the following fields then click Add Source:

    • Name. Укажите понятное имя, например Local-Packages.Name: Provide a meaningful name, for example, Local-Packages.
    • Расположение: Укажите папку локального канала NuGet , созданную на предыдущем шаге.Location: Specify the local-nuget-feed folder created in the previous step.

    Примечание

    В этом случае нет необходимости указывать имя пользователя и пароль.In this case there is no need to specify a Username and Password.

  4. Нажмите кнопку ОК.Click OK.

Указание ссылок на пакетReferencing the package

Повторите следующие шаги для каждого проекта (масфунксапп, масфунксапп. Androidи масфунксапп. iOS).Repeat the following steps for each project (MathFuncsApp, MathFuncsApp.Android, and MathFuncsApp.iOS).

  1. Щелкните проект, а затем выберите Добавить пакеты NuGet... в меню добавить .CONTROL + CLICK on the project, then choose Add NuGet Packages... from the Add menu.
  2. Выполните поиск по запросу масфункс.Search for MathFuncs.
  3. Убедитесь, что версия пакета 1.0.0 , а другие сведения отображаются должным образом, например заголовок и Описание, то есть масфункс и C++ примеры библиотеки оболочек.Verify the Version of the package is 1.0.0 and the other details appear as expected such as the Title and Description, that is, MathFuncs and Sample C++ Wrapper Library.
  4. Выберите пакет масфункс , а затем щелкните Добавить пакет.Select the MathFuncs package, then click Add Package.

Использование библиотечных функцийUsing the library functions

Теперь со ссылкой на пакет масфункс в каждом проекте функции доступны для C# кода.Now, with a reference to the MathFuncs package in each of the projects, the functions are available to the C# code.

  1. Откройте MainPage.XAML.CS в рамках общего проекта Xamarin. Forms масфунксапп (на который ссылаются как масфунксапп. Android , так и масфунксапп. iOS).Open MainPage.xaml.cs from within the MathFuncsApp common Xamarin.Formsproject (referenced by both MathFuncsApp.Android and MathFuncsApp.iOS).

  2. Добавьте операторы using для System. Diagnostics и масфункс в начало файла:Add using statements for System.Diagnostics and MathFuncs at the top of the file:

    using System.Diagnostics;
    using MathFuncs;
    
  3. Объявите экземпляр MyMathFuncs класса в верхней MainPage части класса:Declare an instance of the MyMathFuncs class at the top of the MainPage class:

    MyMathFuncs myMathFuncs;
    
  4. Переопределите методы OnDisappearing ContentPage и из базового класса: OnAppearingOverride the OnAppearing and OnDisappearing methods from the ContentPage base class:

    protected override void OnAppearing()
    {
        base.OnAppearing();
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
    }
    
  5. Обновите myMathFuncs метод, чтобы инициализировать переменную, объявленную ранее: OnAppearingUpdate the OnAppearing method to initialize the myMathFuncs variable declared previously:

    protected override void OnAppearing()
    {
        base.OnAppearing();
        myMathFuncs = new MyMathFuncs();
    }
    
  6. Обновите Dispose метод, чтобы вызвать метод для myMathFuncs: OnDisappearingUpdate the OnDisappearing method to call the Dispose method on myMathFuncs:

    protected override void OnDisappearing()
    {
        base.OnAppearing();
        myMathFuncs.Dispose();
    }
    
  7. Реализуйте закрытый метод с именем тестмасфункс следующим образом:Implement a private method called TestMathFuncs as follows:

    private void TestMathFuncs()
    {
        var numberA = 1;
        var numberB = 2;
    
        // Test Add function
        var addResult = myMathFuncs.Add(numberA, numberB);
    
        // Test Subtract function
        var subtractResult = myMathFuncs.Subtract(numberA, numberB);
    
        // Test Multiply function
        var multiplyResult = myMathFuncs.Multiply(numberA, numberB);
    
        // Test Divide function
        var divideResult = myMathFuncs.Divide(numberA, numberB);
    
        // Output results
        Debug.WriteLine($"{numberA} + {numberB} = {addResult}");
        Debug.WriteLine($"{numberA} - {numberB} = {subtractResult}");
        Debug.WriteLine($"{numberA} * {numberB} = {multiplyResult}");
        Debug.WriteLine($"{numberA} / {numberB} = {divideResult}");
    }
    
  8. Наконец, вызовите TestMathFuncs OnAppearing метод в конце метода:Finally, call TestMathFuncs at the end of the OnAppearing method:

    TestMathFuncs();
    
  9. Запустите приложение на каждой целевой платформе и проверьте выходные данные на панели выходных данных приложения , как показано ниже.Run the app on each target platform and validate the output in the Application Output Pad appears as follows:

    1 + 2 = 3
    1 - 2 = -1
    1 * 2 = 2
    1 / 2 = 0.5
    

    Примечание

    При обнаружении "DLLNotFoundException" при тестировании на Android или при ошибке сборки в iOS убедитесь, что архитектура ЦП устройства, эмулятора и симулятора, которую вы используете, совместима с подмножеством, которое мы выбрали для поддержки.If you encounter a 'DLLNotFoundException' when testing on Android, or a build error on iOS, be sure to check that the CPU architecture of the device/emulator/simulator you are using is compatible with the subset that we chose to support.

СводкаSummary

В этой статье объясняется, как создать приложение Xamarin. Forms, использующее собственные библиотеки, через общую оболочку .NET, распространяемую через пакет NuGet.This article explained how to create a Xamarin.Forms app that uses native libraries through a common .NET wrapper distributed via a NuGet package. Пример, приведенный в этом пошаговом руководстве, намеренно очень упрощен для упрощения демонстрации подхода.The example provided in this walkthrough is intentionally very simplistic to more easily demonstrate the approach. Реальное приложение будет работать со сложными методами, такими как обработка исключений, обратные вызовы, маршалинг более сложных типов и связывание с другими библиотеками зависимостей.A real application will have to deal with complexities such as exception handling, callbacks, the marshalling of more complex types, and linking with other dependency libraries. Ключевым фактором является процесс, с помощью которого развитие C++ кода координируется и синхронизируется с оберткой и клиентскими приложениями.A key consideration is the process by which the evolution of the C++ code is coordinated and synced with the wrapper and client applications. Этот процесс может отличаться в зависимости от того, отвечает ли одна команда на одну или обе.This process may vary depending on whether one or both of those concerns are the responsibility of a single team. В любом случае Автоматизация является реальным преимуществом.Either way, automation is a real benefit. Ниже приведены некоторые ресурсы, которые позволяют более подробно ознакомиться с некоторыми из основных концепций, а также с соответствующими загружаемыми файлами.Below are some resources providing further reading around some of the key concepts along with the relevant downloads.

ЗагрузкиDownloads

ПримерыExamples

Дополнительные сведенияFurther Reading

Статьи, относящиеся к содержимому этой записиArticles relating to the content of this post