Udostępnij za pomocą


Ogólne podklasy obiektu NSObject na platformie Xamarin.iOS

Używanie typów ogólnych z obiektami NSObjects

Można użyć typów ogólnych w podklasach NSObjectklasy , na przykład UIView:

class Foo<T> : UIView {
    public Foo (CGRect x) : base (x) {}
    public override void Draw (CoreGraphics.CGRect rect)
    {
        Console.WriteLine ("T: {0}. Type: {1}", typeof (T), GetType ().Name);
    }
}

Ponieważ obiekty, które są zarejestrowane w podklasie NSObjectObjective-C w środowisku uruchomieniowym, istnieją pewne ograniczenia co do tego, co jest możliwe w przypadku ogólnych podklas NSObject typów.

Zagadnienia dotyczące ogólnych podklas NSObject

Ten dokument zawiera szczegółowe informacje o ograniczeniach dotyczących ograniczonej obsługi podklas ogólnych klasy NSObjects.

Argumenty typu ogólnego w podpisach składowych

Wszystkie argumenty typu ogólnego w podpisie składowym uwidocznione muszą Objective-C mieć NSObject ograniczenie.

Dobre:

class Generic<T> : NSObject where T: NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
    }
}

Przyczyna: Parametr typu ogólnego jest parametrem NSObject, więc sygnatura myMethod: selektora może być bezpiecznie uwidoczniona Objective-C (zawsze będzie NSObject to lub podklasa).

Złe:

class Generic<T> : NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
    }
}

Przyczyna: nie można utworzyć Objective-C podpisu dla wyeksportowanych elementów członkowskich, które Objective-C mogą wywołać kod, ponieważ podpis będzie się różnić w zależności od dokładnego typu typu Togólnego .

Dobre:

class Generic<T> : NSObject
{
    T storage;

    [Export ("myMethod:")]
    public void MyMethod (NSObject value)
    {
    }
}

Przyczyna: istnieje możliwość, że argumenty typu ogólnego nie są ograniczone, o ile nie biorą udziału w wyeksportowanym podpisie elementu członkowskiego.

Dobre:

class Generic<T, U> : NSObject where T: NSObject
{
    [Export ("myMethod:")]
    public void MyMethod (T value)
    {
        Console.WriteLine (typeof (U));
    }
}

PrzyczynaT: parametr wyeksportowany Objective-CMyMethod jest ograniczony jako NSObjecttyp , typ U nie jest częścią podpisu.

Złe:

class Generic<T> : NSObject
{
    public T Storage { get; }

    public Generic(T value)
    {
        Storage = value;
    }
}

[Register("Foo")]
class Foo: NSView
{
    [Export("Test")]
    public Generic<int> Test { get; set; } = new Generic<int>(22);

    [Export("Test1")]
    public Generic<object> Test1 { get; set; } = new Generic<object>(new object());

    [Export("Test2")]
    public Generic<NSObject> Test2 { get; set; } = new Generic<NSObject>(new NSObject());

}

Przyczynaregistrar: nie obsługuje jeszcze tego scenariusza. Aby uzyskać więcej informacji, zobacz te problemy z usługą GitHub.

Tworzenie wystąpień typów ogólnych z Objective-C

Tworzenie wystąpień typów ogólnych z Objective-C programu jest niedozwolone. Zwykle występuje to, gdy typ zarządzany jest używany w xib lub scenorysie.

Rozważmy tę definicję klasy, która uwidacznia konstruktor, który przyjmuje IntPtr element (sposób konstruowania obiektu języka C# z wystąpienia natywnego Objective-C platformy Xamarin.iOS):

class Generic<T> : NSObject where T : NSObject
{
    public Generic () {}
    public Generic (IntPtr ptr) : base (ptr) {}
}

Chociaż powyższa konstrukcja jest w porządku, w czasie wykonywania zgłosi wyjątek, jeśli Objective-C spróbuje utworzyć wystąpienie.

Dzieje się tak, ponieważ Objective-C nie ma pojęcia typów ogólnych i nie może określić dokładnego typu ogólnego do utworzenia.

Ten problem można obejść, tworząc wyspecjalizowaną podklasę typu ogólnego. Na przykład:

class Generic<T> : NSObject where T : NSObject
{
    public Generic () {}
    public Generic (IntPtr ptr) : base (ptr) {}
}

class GenericUIView : Generic<UIView>
{
}

Teraz nie ma już niejednoznaczności, klasa GenericUIView może być używana w xibs lub scenorysach.

Brak obsługi metod ogólnych

Metody ogólne są niedozwolone.

Następujący kod nie zostanie skompilowany:

class MyClass : NSObject
{
    [Export ("myMethod")]
    public void MyMethod<T> (T argument)
    {
    }
}

Przyczyna: Nie jest to dozwolone, ponieważ platforma Xamarin.iOS nie wie, jakiego typu należy użyć dla argumentu T typu, gdy metoda jest wywoływana z Objective-C klasy .

Alternatywą jest utworzenie wyspecjalizowanej metody i wyeksportowanie tej metody:

class MyClass : NSObject
{
    [Export ("myMethod")]
    public void MyUIViewMethod (UIView argument)
    {
        MyMethod<UIView> (argument);
    }
    public void MyMethod<T> (T argument)
    {
    }
}

Nie są dozwolone wyeksportowane statyczne elementy członkowskie

Nie można uwidocznić statycznych elementów członkowskich, Objective-C jeśli są one hostowane w podklasie ogólnej klasy NSObject.

Przykład nieobsługiwanego scenariusza:

class Generic<T> : NSObject where T : NSObject
{
    [Export ("myMethod:")]
    public static void MyMethod ()
    {
    }

    [Export ("myProperty")]
    public static T MyProperty { get; set; }
}

Przyczyna: Podobnie jak metody ogólne, środowisko uruchomieniowe Xamarin.iOS musi wiedzieć, jakiego typu używać dla argumentu Ttypu ogólnego .

Na przykład elementy członkowskie wystąpienia są używane (ponieważ nigdy nie będzie wystąpienia Generic<T>, zawsze będzie Generic<SomeSpecificClass>to ), ale dla statycznych elementów członkowskich te informacje nie są obecne.

Należy pamiętać, że ma to zastosowanie nawet wtedy, gdy element członkowski, którego dotyczy, nie używa argumentu T typu w żaden sposób.

Alternatywą w tym przypadku jest utworzenie wyspecjalizowanej podklasy:

class GenericUIView : Generic<UIView>
{
    [Export ("myUIViewMethod")]
    public static void MyUIViewMethod ()
    {
        MyMethod ();
    }

    [Export ("myProperty")]
    public static UIView MyUIViewProperty {
        get { return MyProperty; }
        set { MyProperty = value; }
    }
}

class Generic<T> : NSObject where T : NSObject
{
    public static void MyMethod () {}
    public static T MyProperty { get; set; }
}

Wydajność

registrar Statyczny element członkowski nie może rozpoznać wyeksportowanego elementu członkowskiego w typie ogólnym w czasie kompilacji, tak jak zwykle, musi być sprawdzany w czasie wykonywania. Oznacza to, że wywoływanie takiej metody z Objective-C metody jest nieco wolniejsze niż wywoływanie elementów członkowskich z klas innych niż ogólne.