Utilisation de types natifs dans des applications multiplateformes

Cet article traite de l’utilisation des nouveaux types natifs d’API unifiée iOS (nint, nuint, nfloat) dans une application multiplateforme où le code est partagé avec des appareils non iOS tels qu’Android ou Windows Téléphone OS.

Les types natifs de 64 types fonctionnent avec les API iOS et Mac. Si vous écrivez également du code partagé qui s’exécute sur Android ou Windows, vous devez gérer la conversion des types unifiés en types .NET standard que vous pouvez partager.

Ce document décrit différentes façons d’interagir avec l’API unifiée à partir de votre code partagé/commun.

Quand utiliser les types natifs

Les API unifiées Xamarin.iOS et Xamarin.Mac incluent toujours les inttypes de uintfloat données, ainsi que les types et PointF les RectangleFSizeF types. Ces types de données existants doivent continuer à être utilisés dans n’importe quel code partagé et multiplateforme. Les nouveaux types de données natifs ne doivent être utilisés que lors de l’appel à une API Mac ou iOS où la prise en charge des types prenant en charge l’architecture est requise.

Selon la nature du code partagé, il peut arriver que le code multiplateforme ait besoin de traiter les types de données et nfloat les nintnuint types de données. Par exemple : une bibliothèque qui gère les transformations sur des données rectangulaires qui utilisaient précédemment System.Drawing.RectangleF pour partager des fonctionnalités entre les versions Xamarin.iOS et Xamarin.Android d’une application, doit être mise à jour pour gérer les types natifs sur iOS.

La façon dont ces modifications sont gérées dépend de la taille et de la complexité de l’application et de la forme de partage de code qui a été utilisé, comme nous le verrons dans les sections suivantes.

Considérations relatives au partage de code

Comme indiqué dans le document Options de code de partage, il existe deux façons principales de partager du code entre des projets multiplateformes : projets partagés et bibliothèques de classes portables. Lequel des deux types a été utilisé, limite les options dont nous disposons lors de la gestion des types de données natifs dans du code multiplateforme.

Projets de bibliothèque de classes portables

Une bibliothèque de classes portable (PCL) vous permet de cibler les plateformes que vous souhaitez prendre en charge et d’utiliser des interfaces pour fournir des fonctionnalités spécifiques à la plateforme.

Étant donné que le type de projet PCL est compilé vers le .DLL bas et qu’il n’a aucun sens de l’API unifiée, vous serez obligé de continuer à utiliser les types de données existants (int, , uintfloat) dans le code source PCL et le type caster les appels aux classes et méthodes de la bibliothèque PCL dans les applications frontales. Par exemple :

using NativePCL;
...

CGRect rect = new CGRect (0, 0, 200, 200);
Console.WriteLine ("Rectangle Area: {0}", Transformations.CalculateArea ((RectangleF)rect));

Projets partagés

Le type de projet de ressource partagée vous permet d’organiser votre code source dans un projet distinct qui est ensuite inclus et compilé dans les applications frontales spécifiques à la plateforme, et d’utiliser #if des directives de compilateur en fonction des besoins spécifiques à la plateforme.

La taille et la complexité des applications mobiles frontales qui consomment du code partagé, ainsi que la taille et la complexité du code partagé, doivent être prises en compte lors du choix de la méthode de prise en charge des types de données natifs dans un projet de ressource partagée multiplateforme.

En fonction de ces facteurs, les types de solutions suivants peuvent être implémentés à l’aide des if __UNIFIED__ ... #endif directives du compilateur pour gérer les modifications spécifiques de l’API unifiée au code.

Utilisation de méthodes dupliquées

Prenons l’exemple d’une bibliothèque qui effectue des transformations sur des données rectangulaires indiquées ci-dessus. Si la bibliothèque ne contient qu’une ou deux méthodes très simples, vous pouvez choisir de créer des versions en double de ces méthodes pour Xamarin.iOS et Xamarin.Android. Par exemple :

using System;
using System.Drawing;

#if __UNIFIED__
using CoreGraphics;
#endif

namespace NativeShared
{
    public class Transformations
    {
        #region Constructors
        public Transformations ()
        {
        }
        #endregion

        #region Public Methods
        #if __UNIFIED__
            public static nfloat CalculateArea(CGRect rect) {

                // Calculate area...
                return (rect.Width * rect.Height);

            }
        #else
            public static float CalculateArea(RectangleF rect) {

                // Calculate area...
                return (rect.Width * rect.Height);

            }
        #endif
        #endregion
    }
}

Dans le code ci-dessus, étant donné que la CalculateArea routine est très simple, nous avons utilisé la compilation conditionnelle et créé une version d’API unifiée distincte de la méthode. En revanche, si la bibliothèque contenait de nombreuses routines ou plusieurs routines complexes, cette solution ne serait pas réalisable, car elle présenterait un problème gardant toutes les méthodes synchronisées pour les modifications ou les correctifs de bogues.

Utilisation de surcharges de méthode

Dans ce cas, la solution peut être de créer une version surchargée des méthodes à l’aide de types de données 32 bits afin qu’elles prennent CGRect désormais comme paramètre et/ou une valeur de retour, convertissent cette valeur en une RectangleF (sachant que la conversion nfloat en float est une conversion de perte) et appelez la version d’origine de la routine pour effectuer le travail réel. Par exemple :

using System;
using System.Drawing;

#if __UNIFIED__
using CoreGraphics;
#endif

namespace NativeShared
{
    public class Transformations
    {
        #region Constructors
        public Transformations ()
        {
        }
        #endregion

        #region Public Methods
        #if __UNIFIED__
            public static nfloat CalculateArea(CGRect rect) {

                // Call original routine to calculate area
                return (nfloat)CalculateArea((RectangleF)rect);

            }
        #endif

        public static float CalculateArea(RectangleF rect) {

            // Calculate area...
            return (rect.Width * rect.Height);

        }

        #endregion
    }
}

Là encore, il s’agit d’une bonne solution tant que la perte de précision n’affecte pas les résultats pour les besoins spécifiques de votre application.

Utilisation des directives d’alias

Pour les zones où la perte de précision est un problème, une autre solution possible consiste à utiliser using des directives pour créer un alias pour les types de données Native et CoreGraphics en incluant le code suivant en haut des fichiers de code source partagés et en convertissant les valeurs nécessairesint, uint ou float en nuintnintnfloat:

#if __UNIFIED__
    // Mappings Unified CoreGraphic classes to MonoTouch classes
    using RectangleF = global::CoreGraphics.CGRect;
    using SizeF = global::CoreGraphics.CGSize;
    using PointF = global::CoreGraphics.CGPoint;
#else
    // Mappings Unified types to MonoTouch types
    using nfloat = global::System.Single;
    using nint = global::System.Int32;
    using nuint = global::System.UInt32;
#endif

Ainsi, notre exemple de code devient :

using System;
using System.Drawing;

#if __UNIFIED__
    // Map Unified CoreGraphic classes to MonoTouch classes
    using RectangleF = global::CoreGraphics.CGRect;
    using SizeF = global::CoreGraphics.CGSize;
    using PointF = global::CoreGraphics.CGPoint;
#else
    // Map Unified types to MonoTouch types
    using nfloat = global::System.Single;
    using nint = global::System.Int32;
    using nuint = global::System.UInt32;
#endif

namespace NativeShared
{

    public class Transformations
    {
        #region Constructors
        public Transformations ()
        {
        }
        #endregion

        #region Public Methods
        public static nfloat CalculateArea(RectangleF rect) {

            // Calculate area...
            return (rect.Width * rect.Height);

        }
        #endregion
    }
}

Notez que ici, nous avons modifié la CalculateArea méthode pour retourner une nfloat valeur au lieu de la norme float. Cette opération a été effectuée afin que nous n’obtenions pas d’erreur de compilation essayant de convertir implicitement le nfloat résultat de notre calcul (étant donné que les deux valeurs multipliées sont de type nfloat) en une float valeur de retour.

Si le code est compilé et exécuté sur un appareil d’API non unifié, celui-ci using nfloat = global::System.Single;nfloat est mappé à un Single qui est implicitement converti en une float autorisation permettant à l’application frontale consommatrice d’appeler la CalculateArea méthode sans modification.

Utilisation des conversions de types dans l’application frontale

Si vos applications frontales n’effectuent qu’une poignée d’appels à votre bibliothèque de code partagée, une autre solution peut être de laisser la bibliothèque inchangée et d’effectuer un cast de type dans l’application Xamarin.iOS ou Xamarin.Mac lors de l’appel de la routine existante. Par exemple :

using NativeShared;
...

CGRect rect = new CGRect (0, 0, 200, 200);
Console.WriteLine ("Rectangle Area: {0}", Transformations.CalculateArea ((RectangleF)rect));

Si l’application consommatrice effectue des centaines d’appels à la bibliothèque de code partagée, cela peut ne pas être une bonne solution.

En fonction de l’architecture de notre application, nous pouvons finir par utiliser une ou plusieurs des solutions ci-dessus pour prendre en charge les types de données natifs (si nécessaire) dans notre code multiplateforme.

Xamarin.Forms Applications

Vous devez utiliser Xamarin.Forms pour les interfaces utilisateur multiplateformes qui seront également partagées avec une application d’API unifiée :

  • La solution entière doit utiliser la version 1.3.1 (ou ultérieure) du package NuGet Xamarin.Forms.
  • Pour tout rendu personnalisé Xamarin.iOS, utilisez les mêmes types de solutions présentés ci-dessus en fonction de la façon dont le code de l’interface utilisateur a été partagé (Projet partagé ou PCL).

Comme dans une application multiplateforme standard, les types de données 32 bits existants doivent être utilisés dans n’importe quel code partagé et multiplateforme pour la plupart des situations. Les nouveaux types de données natifs doivent être utilisés uniquement lors de l’appel à une API Mac ou iOS où la prise en charge des types prenant en charge l’architecture est requise.

Pour plus d’informations, consultez notre documentation sur la mise à jour des applications Xamarin.Forms existantes.

Résumé

Dans cet article, nous avons vu quand utiliser les types de données natifs dans une application d’API unifiée et leurs implications entre plateformes. Nous avons présenté plusieurs solutions qui peuvent être utilisées dans des situations où les nouveaux types de données natifs doivent être utilisés dans des bibliothèques multiplateformes. En outre, nous avons vu un guide rapide pour prendre en charge les API unifiées dans les applications multiplateformes Xamarin.Forms.