Praca z typami natywnymi w aplikacjach międzyplatformowych
W tym artykule omówiono używanie nowych typów natywnych interfejsu API ujednoliconego interfejsu API systemu iOS (nint, nuint, nfloat) w aplikacji międzyplatformowej, w której kod jest udostępniany urządzeniom innym niż iOS, takim jak systemy operacyjne Android lub Windows Telefon.
Typy natywne 64 działają z interfejsami API systemów iOS i Mac. Jeśli piszesz kod udostępniony, który działa również w systemie Android lub Windows, musisz zarządzać konwersją typów Unified na zwykłe typy platformy .NET, które można udostępniać.
W tym dokumencie omówiono różne sposoby współdziałania z ujednoliconym interfejsem API z udostępnionego/wspólnego kodu.
Kiedy używać typów natywnych
Interfejsy API platform Xamarin.iOS i Xamarin.Mac Unified nadal obejmują int
uint
typy danych i float
, a także RectangleF
SizeF
typy i PointF
. Te istniejące typy danych powinny być nadal używane w dowolnym udostępnionym kodzie międzyplatformowym. Nowe natywne typy danych powinny być używane tylko podczas wywoływania interfejsu API dla komputerów Mac lub iOS, w którym wymagana jest obsługa typów obsługujących architekturę.
W zależności od charakteru udostępnianego kodu mogą wystąpić czasy, w których kod międzyplatformowy może wymagać obsługi nint
typów danych i nfloat
. nuint
Na przykład: biblioteka, która obsługuje przekształcenia na prostokątnych danych, których wcześniej używano System.Drawing.RectangleF
do udostępniania funkcji między wersjami platform Xamarin.iOS i Xamarin.Android aplikacji, musi zostać zaktualizowana w celu obsługi typów natywnych w systemie iOS.
Sposób obsługi tych zmian zależy od rozmiaru i złożoności aplikacji oraz formy udostępniania kodu, jak pokazano w poniższych sekcjach.
Zagadnienia dotyczące udostępniania kodu
Jak opisano w dokumencie Opcje kodu udostępniania, istnieją dwa główne sposoby udostępniania kodu między projektami międzyplatformowymi: Udostępnione projekty i przenośne biblioteki klas. Które z tych dwóch typów zostały użyte, ograniczy opcje, które mamy podczas obsługi natywnych typów danych w kodzie międzyplatformowym.
Przenośne projekty bibliotek klas
Przenośna biblioteka klas (PCL) umożliwia określanie platform, które mają być obsługiwane, i korzystanie z interfejsów w celu zapewnienia funkcji specyficznych dla platformy.
Ponieważ typ projektu PCL jest kompilowany w dół do .DLL
elementu i nie ma sensu ujednoliconego interfejsu API, musisz nadal używać istniejących typów danych (int
, uint
, float
) w kodzie źródłowym PCL i typ rzutować wywołania do klas i metod PCL w aplikacjach frontonu. Na przykład:
using NativePCL;
...
CGRect rect = new CGRect (0, 0, 200, 200);
Console.WriteLine ("Rectangle Area: {0}", Transformations.CalculateArea ((RectangleF)rect));
Projekty udostępnione
Typ projektu udostępnionego zasobu umożliwia organizowanie kodu źródłowego w osobnym projekcie, który następnie zostanie uwzględniony i skompilowany w poszczególnych aplikacjach frontonu specyficznych dla platformy, i użyj #if
dyrektyw kompilatora zgodnie z wymaganiami specyficznymi dla platformy.
Rozmiar i złożoność aplikacji mobilnych frontonu korzystających z kodu udostępnionego wraz z rozmiarem i złożonością współużytkowanego kodu należy wziąć pod uwagę podczas wybierania metody obsługi natywnych typów danych w projekcie udostępnionych zasobów międzyplatformowych.
Na podstawie tych czynników następujące typy rozwiązań mogą być implementowane przy użyciu if __UNIFIED__ ... #endif
dyrektyw kompilatora w celu obsługi zmian specyficznych dla ujednoliconego interfejsu API w kodzie.
Używanie zduplikowanych metod
Zapoznaj się z przykładem biblioteki wykonującej przekształcenia na prostokątnych danych podanych powyżej. Jeśli biblioteka zawiera tylko jedną lub dwie bardzo proste metody, możesz utworzyć zduplikowane wersje tych metod dla platform Xamarin.iOS i Xamarin.Android. Na przykład:
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
}
}
W powyższym kodzie, ponieważ rutynowa CalculateArea
jest bardzo prosta, użyliśmy kompilacji warunkowej i utworzyliśmy oddzielną, ujednoliconą wersję interfejsu API metody . Z drugiej strony, jeśli biblioteka zawierała wiele procedur lub kilka złożonych procedur, to rozwiązanie nie byłoby możliwe, ponieważ stanowiłoby to problem z zachowaniem synchronizacji wszystkich metod w celu modyfikacji lub poprawek błędów.
Używanie przeciążeń metody
W takim przypadku rozwiązaniem może być utworzenie przeciążonej wersji metod przy użyciu typów danych 32-bitowych, aby teraz przyjmować CGRect
jako parametr i/lub wartość zwracaną, konwertować wartość na RectangleF
(wiedząc, że konwersja z nfloat
na float
jest konwersją stratową) i wywołać oryginalną wersję procedury, aby wykonać rzeczywistą pracę. Na przykład:
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
}
}
Ponownie jest to dobre rozwiązanie, o ile utrata precyzji nie ma wpływu na wyniki dla konkretnych potrzeb aplikacji.
Używanie dyrektyw aliasu
W przypadku obszarów, w których utrata precyzji jest problemem, innym możliwym rozwiązaniem jest użycie using
dyrektyw w celu utworzenia aliasu dla typów danych Native i CoreGraphics przez dołączenie następującego kodu na początku udostępnionych plików kodu źródłowego i przekonwertowanie wszelkich wymaganych float
int
uint
wartości nint
na , nuint
lub nfloat
:
#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
Tak więc nasz przykładowy kod staje się:
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
}
}
Pamiętaj, że w tym miejscu zmieniliśmy metodę CalculateArea
, aby zwrócić wartość nfloat
zamiast standardu float
. Zostało to zrobione, aby nie można było uzyskać błędu kompilacji próbującego niejawnie przekonwertować nfloat
wynik obliczenia (ponieważ obie wartości są mnożone są typu nfloat
) na wartość zwracanąfloat
.
Jeśli kod jest kompilowany i uruchamiany na urządzeniu bez ujednoliconego interfejsu API, mapuje nfloat
obiekt na element Single
, using nfloat = global::System.Single;
który niejawnie konwertuje na aplikację frontonu, która umożliwia korzystanie z float
aplikacji frontonu CalculateArea
w celu wywołania metody bez modyfikacji.
Używanie konwersji typów w aplikacji frontonu
W przypadku, gdy aplikacje frontonu wykonują tylko kilka wywołań do udostępnionej biblioteki kodu, innym rozwiązaniem może być pozostawienie biblioteki bez zmian i wykonanie rzutowania typów w aplikacji Xamarin.iOS lub Xamarin.Mac podczas wywoływania istniejącej procedury. Na przykład:
using NativeShared;
...
CGRect rect = new CGRect (0, 0, 200, 200);
Console.WriteLine ("Rectangle Area: {0}", Transformations.CalculateArea ((RectangleF)rect));
Jeśli aplikacja korzystająca wykonuje setki wywołań do udostępnionej biblioteki kodu, może to nie być dobrym rozwiązaniem.
Na podstawie architektury aplikacji możemy skorzystać z jednego lub kilku powyższych rozwiązań do obsługi natywnych typów danych (tam, gdzie jest to wymagane) w naszym kodzie międzyplatformowym.
Aplikacje platformy Xamarin.Forms
Do używania zestawu narzędzi Xamarin.Forms dla interfejsów użytkownika międzyplatformowych, które będą również udostępniane aplikacji ujednoliconego interfejsu API:
- Całe rozwiązanie musi używać wersji 1.3.1 (lub nowszej) pakietu NuGet platformy Xamarin.Forms.
- W przypadku dowolnych niestandardowych renderów platformy Xamarin.iOS użyj tych samych typów rozwiązań przedstawionych powyżej w oparciu o sposób udostępniania kodu interfejsu użytkownika (udostępniony projekt lub PCL).
Podobnie jak w standardowej aplikacji międzyplatformowej, istniejące 32-bitowe typy danych powinny być używane w dowolnym udostępnionym kodzie międzyplatformowym w większości sytuacji. Nowe typy danych natywnych powinny być używane tylko podczas wywoływania interfejsu API dla komputerów Mac lub iOS, w którym wymagana jest obsługa typów obsługujących architekturę.
Aby uzyskać więcej informacji, zobacz dokumentację Aktualizowanie istniejących aplikacji platformy Xamarin.Forms.
Podsumowanie
W tym artykule pokazano, kiedy używać natywnych typów danych w aplikacji ujednoliconego interfejsu API i ich implikacji międzyplatformowych. Przedstawiliśmy kilka rozwiązań, które mogą być używane w sytuacjach, w których nowe natywne typy danych muszą być używane w bibliotekach międzyplatformowych. Przedstawiono również szybki przewodnik dotyczący obsługi ujednoliconych interfejsów API w aplikacjach międzyplatformowych platform Xamarin.Forms.