Teilen über


Arbeiten mit nativen Typen in plattformübergreifenden Apps

In diesem Artikel wird die Verwendung der neuen nativen iOS-API-Typen (nint, nuint, nfloat) in einer plattformübergreifenden Anwendung behandelt, in der Code für Nicht-iOS-Geräte wie Android oder Windows Telefon OSes freigegeben wird.

Die 64 Typen nativer Typen funktionieren mit den iOS- und Mac-APIs. Wenn Sie gemeinsam genutzten Code schreiben, der auch unter Android oder Windows ausgeführt wird, müssen Sie die Konvertierung von Unified-Typen in reguläre .NET-Typen verwalten, die Sie freigeben können.

In diesem Dokument werden verschiedene Möglichkeiten für die Zusammenarbeit mit der einheitlichen API aus Ihrem freigegebenen/gemeinsamen Code erläutert.

Wann die nativen Typen verwendet werden sollen

Xamarin.iOS- und Xamarin.Mac Unified-APIs enthalten weiterhin die intDatentypen uint sowie float die RectangleFTypen SizeF und PointF Typen. Diese vorhandenen Datentypen sollten weiterhin in allen gemeinsam genutzten plattformübergreifenden Code verwendet werden. Die neuen nativen Datentypen sollten nur verwendet werden, wenn sie einen Aufruf an eine Mac- oder iOS-API durchführen, bei der Unterstützung für architekturfähige Typen erforderlich sind.

Je nach Art des freigegebenen Codes kann es vorkommen, dass plattformübergreifender Code möglicherweise mit den nintDatentypen nuint und nfloat Datentypen umgehen muss. Beispiel: Eine Bibliothek, die Transformationen für rechteckige Daten verarbeitet, die zuvor zum Freigeben von Funktionen zwischen Xamarin.iOS- und Xamarin.Android-Versionen einer App verwendet System.Drawing.RectangleF wurden, müsste aktualisiert werden, um native Typen unter iOS zu verarbeiten.

Wie diese Änderungen behandelt werden, hängt von der Größe und Komplexität der Anwendung und der Form der verwendeten Codefreigabe ab, wie in den folgenden Abschnitten gezeigt wird.

Überlegungen zur Codefreigabe

Wie im Dokument "Freigabecodeoptionen" angegeben, gibt es zwei Standard Möglichkeiten zum Teilen von Code zwischen plattformübergreifenden Projekten: Freigegebene Projekte und portable Klassenbibliotheken. Welche der beiden Typen verwendet wurde, schränkt die Optionen ein, die wir bei der Behandlung der nativen Datentypen im plattformübergreifenden Code haben.

Portable Klassenbibliotheksprojekte

Mit einer portablen Klassenbibliothek (PORTABLE Class Library, PCL) können Sie auf die Plattformen abzielen, die Sie unterstützen möchten, und Schnittstellen verwenden, um plattformspezifische Funktionen bereitzustellen.

Da der PCL-Projekttyp in einen .DLL Kompiliert wird und es keinen Sinn der Unified API hat, werden Sie gezwungen, die vorhandenen Datentypen (int, , uint) floatim PCL-Quellcode zu verwenden und die Aufrufe der KLASSEN und Methoden der PCL in die Front-End-Anwendungen zu umwandeln. Zum Beispiel:

using NativePCL;
...

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

Freigegebene Projekte

Mit dem Projekttyp "Freigegebene Ressourcen" können Sie Den Quellcode in einem separaten Projekt organisieren, das dann in die einzelnen plattformspezifischen Front-End-Apps eingeschlossen und kompiliert wird, und Compilerdirektiven #if verwenden, um plattformspezifische Anforderungen zu verwalten.

Die Größe und Komplexität der mobilen Front-End-Anwendungen, die gemeinsam genutzten Code verwenden, sowie die Größe und Komplexität des gemeinsam genutzten Codes müssen bei der Auswahl der Unterstützungsmethode für native Datentypen in einem plattformübergreifenden Freigegebenen Objektprojekt berücksichtigt werden.

Basierend auf diesen Faktoren können die folgenden Lösungstypen mithilfe der if __UNIFIED__ ... #endif Compilerdirektiven implementiert werden, um die spezifischen Änderungen der Unified API für den Code zu verarbeiten.

Verwenden doppelter Methoden

Nehmen Sie sich das Beispiel einer Bibliothek an, in der Transformationen für rechteckige Daten ausgeführt werden, die oben angegeben sind. Wenn die Bibliothek nur eine oder zwei sehr einfache Methoden enthält, können Sie doppelte Versionen dieser Methoden für Xamarin.iOS und Xamarin.Android erstellen. Zum Beispiel:

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
    }
}

Im obigen Code, da die CalculateArea Routine sehr einfach ist, haben wir die bedingte Kompilierung verwendet und eine separate, Einheitliche API-Version der Methode erstellt. Wenn die Bibliothek andererseits viele Routinen oder mehrere komplexe Routinen enthielt, wäre diese Lösung nicht machbar, da es ein Problem darstellen würde, das alle Methoden für Änderungen oder Fehlerbehebungen synchronisiert.

Verwenden von Methodenüberladungen

In diesem Fall kann die Lösung darin bestehen, eine Überladungsversion der Methoden mit 32-Bit-Datentypen zu erstellen, damit sie jetzt als Parameter und/oder Rückgabewert verwendet CGRect werden, diesen Wert in einen RectangleF konvertieren (wissen, dass die Konvertierung von nfloat zu float einer verlustbehafteten Konvertierung ist), und rufen Sie die ursprüngliche Version der Routine auf, um die eigentliche Arbeit auszuführen. Zum Beispiel:

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
    }
}

Auch hier ist dies eine gute Lösung, solange sich der Genauigkeitsverlust nicht auf die Ergebnisse für die spezifischen Anforderungen Ihrer Anwendung auswirkt.

Verwenden von Aliasdirektiven

Für Bereiche, in denen der Genauigkeitsverlust ein Problem darstellt, besteht eine weitere mögliche Lösung darin, Direktiven zu verwendenusing, um einen Alias für native und CoreGraphics-Datentypen zu erstellen, indem sie den folgenden Code oben in die freigegebenen Quellcodedateien einschließen und alle erforderlichen int, uint oder Werte in , nuint oder floatnfloat:nint

#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

So wird unser Beispielcode dann zu:

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
    }
}

Beachten Sie, dass wir hier die CalculateArea Methode so geändert haben, dass anstelle des Standards floatein nfloat Wert zurückgegeben wird. Dies wurde getan, damit wir keinen Kompilierungsfehler erhalten würden, der versucht, das nfloat Ergebnis unserer Berechnung implizit zu konvertieren (da beide Werte multipliziert werden) nfloatin einen float Rückgabewert.

Wenn der Code kompiliert und auf einem nicht einheitlichen API-Gerät ausgeführt wird, wird der using nfloat = global::System.Single;nfloat Code einem Single zugeordnet, der implizit in eine float fähige Front-End-Anwendung konvertiert wird, um die CalculateArea Methode ohne Änderung aufzurufen.

Verwenden von Typkonvertierungen in der Front-End-App

Wenn Ihre Front-End-Anwendungen nur eine Handvoll Aufrufe an Ihre freigegebene Codebibliothek ausführen, könnte eine andere Lösung darin bestehen, die Bibliothek unverändert zu lassen und beim Aufrufen der vorhandenen Routine ein Umwandlungstyp in die Anwendung Xamarin.iOS oder Xamarin.Mac durchzuführen. Zum Beispiel:

using NativeShared;
...

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

Wenn die verbrauchende Anwendung Hunderte von Aufrufen an die freigegebene Codebibliothek durchführt, ist dies möglicherweise keine gute Lösung.

Basierend auf der Architektur unserer Anwendung verwenden wir möglicherweise eine oder mehrere der oben genannten Lösungen, um native Datentypen (sofern erforderlich) in unserem plattformübergreifenden Code zu unterstützen.

Xamarin.Forms-Anwendungen

Es folgt die Verwendung von Xamarin.Forms für plattformübergreifende UIs, die auch für eine Unified API-Anwendung freigegeben werden:

  • Die gesamte Lösung muss Version 1.3.1 (oder höher) des Xamarin.Forms NuGet-Pakets verwenden.
  • Verwenden Sie für alle benutzerdefinierten Xamarin.iOS-Rendervorgänge dieselben Lösungstypen, die oben dargestellt werden, basierend darauf, wie der Benutzeroberflächencode freigegeben wurde (Freigegebenes Projekt oder PCL).

Wie bei einer plattformübergreifenden Standardanwendung sollten die vorhandenen 32-Bit-Datentypen in allen gemeinsam genutzten plattformübergreifenden Code für die meisten Situationen verwendet werden. Die neuen nativen Datentypen sollten nur verwendet werden, wenn sie einen Aufruf an eine Mac- oder iOS-API durchführen, bei der Unterstützung für architekturbezogene Typen erforderlich ist.

Weitere Informationen finden Sie in der Dokumentation "Existing Xamarin.Forms Apps ".

Zusammenfassung

In diesem Artikel haben wir gesehen, wann die nativen Datentypen in einer Unified API-Anwendung und deren Auswirkungen plattformübergreifend verwendet werden. Wir haben mehrere Lösungen vorgestellt, die in Situationen verwendet werden können, in denen die neuen nativen Datentypen in plattformübergreifenden Bibliotheken verwendet werden müssen. Außerdem haben wir einen Kurzleitfaden zur Unterstützung von Unified APIs in plattformübergreifenden Xamarin.Forms-Anwendungen gesehen.