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 int
Datentypen uint
sowie float
die RectangleF
Typen 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 nint
Datentypen 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
) float
im 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 float
nfloat
: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 float
ein 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) nfloat
in 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.