Udostępnij za pośrednictwem


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ą intuint typy danych i float , a także RectangleFSizeF 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 ninttypó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 floatintuint wartości nintna , 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.