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 NSObject
parametr , 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 T
obecné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.