Obecné podtřídy objektu NSObject v Xamarin.iOS

Použití obecných typů s objekty NSObjects

Obecné typy je možné použít v podtřídách NSObject, například 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);
    }
}

Vzhledem k tomu, že objekty, které podtřídy NSObject jsou registrovány v Objective-C modulu runtime, existují určitá omezení, co je možné s obecnými podtřídami NSObject typů.

Důležité informace o obecných podtřídách objektu NSObject

Tento dokument podrobně popisuje omezení omezené podpory obecných podtříd .NSObjects

Argumenty obecného typu v podpisech členů

Všechny argumenty obecného typu v podpisu člena vystavené Objective-C musí mít NSObject omezení.

Dobré:

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

Důvod: Parametr obecného typu je NSObjectparametr , takže podpis selektoru pro myMethod: může být bezpečně vystaven Objective-C (bude vždy NSObject nebo podtřídou).

Špatné:

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

Důvod: není možné vytvořit Objective-C podpis pro exportované členy, které Objective-C kód může volat, protože podpis by se liší v závislosti na přesném typu obecného typu T.

Dobré:

class Generic<T> : NSObject
{
    T storage;

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

Důvod: Je možné mít nekontrénované argumenty obecného typu, pokud nejsou součástí exportovaného podpisu člena.

Dobré:

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

Důvod: Parametr v Objective-C exportu MyMethod je omezen na NSObject, unconstrained type U is not part T of the signature.

Špatné:

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());

}

Důvod: registrar Tento scénář zatím nepodporuje. Další informace najdete v těchto problémech na GitHubu.

Vytváření instancí obecných typů z Objective-C

Vytvoření instance obecných typů z Objective-C není povoleno. K tomu obvykle dochází v případě, že se spravovaný typ používá v xib nebo scénáře.

Vezměte v úvahu tuto definici třídy, která zpřístupňuje konstruktor, který přebírá IntPtr (způsob vytváření objektu jazyka C# z nativní Objective-C instance Xamarin.iOS):

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

Zatímco výše uvedený konstruktor je v pořádku, za běhu vyvolá výjimku, pokud Objective-C se pokusí vytvořit instanci.

K tomu dochází, protože Objective-C nemá žádný koncept obecných typů a nemůže určit přesný obecný typ, který se má vytvořit.

Tento problém lze obejít vytvořením specializované podtřídy obecného typu. Příklad:

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

class GenericUIView : Generic<UIView>
{
}

Teď už neexistuje žádná nejednoznačnost, třídu GenericUIView lze použít v xibs nebo scénářích.

Žádná podpora obecných metod

Obecné metody nejsou povoleny.

Následující kód nebude kompilován:

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

Důvod: Toto není povoleno, protože Xamarin.iOS neví, který typ se má použít pro argument T typu při vyvolání metody z Objective-C .

Alternativou je vytvoření specializované metody a exportu, která místo toho:

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

Nejsou povoleny žádné exportované statické členy.

Statické členy nelze zveřejnit Objective-C , pokud je hostováno v obecné podtřídě NSObject.

Příklad nepodporovaného scénáře:

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

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

Důvod: Stejně jako obecné metody musí modul runtime Xamarin.iOS vědět, jaký typ se má použít pro argument Tobecného typu .

Například členové samotné instance se používají (protože nikdy nebude existovat instance Generic<T>, bude vždy Generic<SomeSpecificClass>), ale pro statické členy tyto informace nejsou přítomny.

Všimněte si, že to platí i v případě, že daný člen nepoužívá argument T typu žádným způsobem.

Alternativou v tomto případě je vytvoření specializované podtřídy:

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

Výkon

Statická registrar nemůže vyřešit exportovaný člen v obecném typu v době sestavení, jak to obvykle dělá, musí být vyhledána za běhu. To znamená, že vyvolání takové metody z Objective-C je o něco pomalejší než vyvolání členů z ne generických tříd.